Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.*;
/**
* Verifies that constants are only assigned a value once.
* e.g. var XX = 5;
* XX = 3; // error!
* XX++; // error!
*
*/
class ConstCheck extends AbstractPostOrderCallback
implements CompilerPass {
static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR =
DiagnosticType.error(
"JSC_CONSTANT_REASSIGNED_VALUE_ERROR",
"constant {0} assigned a value more than once");
private final AbstractCompiler compiler;
private final Set<Scope.Var> initializedConstants;
/**
* Creates an instance.
*/
public ConstCheck(AbstractCompiler compiler) {
this.compiler = compiler;
this.initializedConstants = new HashSet<Scope.Var>();
}
@Override
public void process(Node externs, Node root) {
Preconditions.checkState(compiler.getLifeCycleStage().isNormalized());
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
if (parent != null &&
parent.isVar() &&
n.hasChildren()) {
String name = n.getString();
Scope.Var var =
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Convention.getObjectLiteralCast(callNode);
}
@Override
public Collection<String> getIndirectlyDeclaredProperties() {
return nextConvention.getIndirectlyDeclaredProperties();
}
}
/**
* The default coding convention.
* Should be at the bottom of all proxy chains.
*/
private static class DefaultCodingConvention implements CodingConvention {
private static final long serialVersionUID = 1L;
@Override
public boolean isConstant(String variableName) {
return false;
}
@Override
public boolean isConstantKey(String variableName) {
return false;
}
@Override
public boolean isValidEnumKey(String key) {
return key != null && key.length() > 0;
}
@Override
public boolean isOptionalParameter(Node parameter) {
// be as lax as possible, but this must be mutually exclusive from
// var_args parameters.
return !isVarArgsParameter(parameter);
}
@Override
public boolean isVarArgsParameter(Node parameter) {
// be as lax as possible
return parameter.getParent().getLastChild() == parameter;
}
@Override
public boolean isExported(String name, boolean local) {
return local && name.startsWith("$super");
}
@Override
public boolean isExported(String name) {
return isExported(name, false) || isExported(name, true);
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public SubclassRelationship getClassesDefinedByCall(Node callNode) {
return null;
}
@Override
public boolean isSuperClassReference(String propertyName) {
return false;
}
@Override
public String extractClassNameIfProvide(Node node, Node parent) {
String message = "only implemented in GoogleCodingConvention";
throw new UnsupportedOperationException(message);
}
@Override
public String extractClassNameIfRequire(Node node, Node parent) {
String message = "only implemented in GoogleCodingConvention";
throw new UnsupportedOperationException(message);
}
@Override
public String getExportPropertyFunction() {
return null;
}
@Override
public String getExportSymbolFunction() {
return null;
}
@Override
public List<String> identifyTypeDeclarationCall(Node n) {
return null;
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> @Override
public void applySubclassRelationship(FunctionType parentCtor,
FunctionType childCtor, SubclassType type) {
// do nothing
}
@Override
public String getAbstractMethodName() {
return null;
}
@Override
public String getSingletonGetterClassName(Node callNode) {
return null;
}
@Override
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
// do nothing.
}
@Override
public boolean isInlinableFunction(Node n) {
Preconditions.checkState(n.isFunction());
return true;
}
@Override
public DelegateRelationship getDelegateRelationship(Node callNode) {
return null;
}
@Override
public void applyDelegateRelationship(
ObjectType delegateSuperclass, ObjectType delegateBase,
ObjectType delegator, FunctionType delegateProxy,
FunctionType findDelegate) {
// do nothing.
}
@Override
public String getDelegateSuperclassName() {
return null;
}
@Override
public void checkForCallingConventionDefiningCalls(Node n,
Map<String, String> delegateCallingConventions) {
// do nothing.
}
@Override
public void defineDelegateProxyPrototypeProperties(
JSTypeRegistry registry, StaticScope<JSType> scope,
List<ObjectType> delegateProxyPrototypes,
Map<String, String> delegateCallingConventions) {
// do nothing.
}
@Override
public String getGlobalObject() {
return "window";
}
@Override
public boolean isPropertyTestFunction(Node call) {
return false;
}
@Override
public boolean isPrototypeAlias(Node getProp) {
return false;
}
@Override
public ObjectLiteralCast getObjectLiteralCast(Node callNode) {
return null;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return Collections.emptySet();
}
@Override
public Bind describeFunctionBind(Node n) {
return describeFunctionBind(n, false);
}
@Override
public Bind describeFunctionBind(Node n, boolean useTypeInfo) {
if (!n.isCall()) {
return null;
}
Node callTarget = n.getFirstChild();
String name = callTarget.getQualifiedName();
if (name != null) {
if (name
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.debugging.sourcemap.FilePosition;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
/**
* CodePrinter prints out JS code in either pretty format or compact format.
*
* @see CodeGenerator
*/
class CodePrinter {
// The number of characters after which we insert a line break in the code
static final int DEFAULT_LINE_LENGTH_THRESHOLD = 500;
// There are two separate CodeConsumers, one for pretty-printing and
// another for compact printing.
// There are two implementations because the CompactCodePrinter
// potentially has a very different implementation to the pretty
// version.
private abstract static class MappedCodePrinter extends CodeConsumer {
final private Deque<Mapping> mappings;
final private List<Mapping> allMappings;
final private boolean createSrcMap;
final private SourceMap.DetailLevel sourceMapDetailLevel;
protected final StringBuilder code = new StringBuilder(1024);
protected final int lineLengthThreshold;
protected int lineLength = 0;
protected int lineIndex = 0;
MappedCodePrinter(
int lineLengthThreshold,
boolean createSrcMap,
SourceMap.DetailLevel sourceMapDetailLevel) {
Preconditions.checkState(sourceMapDetailLevel != null);
this
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.lineLengthThreshold = lineLengthThreshold <= 0 ? Integer.MAX_VALUE :
lineLengthThreshold;
this.createSrcMap = createSrcMap;
this.sourceMapDetailLevel = sourceMapDetailLevel;
this.mappings = createSrcMap ? new ArrayDeque<Mapping>() : null;
this.allMappings = createSrcMap ? new ArrayList<Mapping>() : null;
}
/**
* Maintains a mapping from a given node to the position
* in the source code at which its generated form was
* placed. This position is relative only to the current
* run of the CodeConsumer and will be normalized
* later on by the SourceMap.
*
* @see SourceMap
*/
private static class Mapping {
Node node;
FilePosition start;
FilePosition end;
}
/**
* Starts the source mapping for the given
* node at the current position.
*/
@Override
void startSourceMapping(Node node) {
Preconditions.checkState(sourceMapDetailLevel != null);
Preconditions.checkState(node != null);
if (createSrcMap
&& node.getSourceFileName() != null
&& node.getLineno() > 0
&& sourceMapDetailLevel.apply(node)) {
int line = getCurrentLineIndex();
int index = getCurrentCharIndex();
Preconditions.checkState(line >= 0);
Mapping mapping = new Mapping();
mapping.node = node;
mapping.start = new FilePosition(line, index);
mappings.push(mapping);
allMappings.add(mapping);
}
}
/**
* Finishes the source mapping for the given
* node at the current position.
*/
@Override
void endSourceMapping(Node node) {
if (createSrcMap && !mappings.isEmpty() && mappings.peek().node == node) {
Mapping mapping = mappings.pop();
int line = getCurrentLineIndex();
int index = getCurrentCharIndex();
Preconditions.checkState(line >= 0);
mapping.end = new FilePosition(line, index);
}
}
/**
* Generates the source map from the given code consumer,
* appending the information it saved to the SourceMap
* object given.
*/
void generateSourceMap(SourceMap map){
if (createSrcMap) {
for (Mapping mapping
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
@Override
void maybeLineBreak() {
maybeCutLine();
}
/**
* This may start a new line if the current line is longer than the line
* length threshold.
*/
@Override
void maybeCutLine() {
if (lineLength > lineLengthThreshold) {
startNewLine();
}
}
@Override
void endLine() {
startNewLine();
}
@Override
void appendBlockStart() {
append(" {");
indent++;
}
@Override
void appendBlockEnd() {
endLine();
indent--;
append("}");
}
@Override
void listSeparator() {
add(", ");
maybeLineBreak();
}
@Override
void endFunction(boolean statementContext) {
super.endFunction(statementContext);
if (statementContext) {
startNewLine();
}
}
@Override
void beginCaseBody() {
super.beginCaseBody();
indent++;
endLine();
}
@Override
void endCaseBody() {
super.endCaseBody();
indent--;
endStatement();
}
@Override
void appendOp(String op, boolean binOp) {
if (binOp) {
if (getLastChar() != ' ' && op.charAt(0) != ',') {
append(" ");
}
append(op);
append(" ");
} else {
append(op);
}
}
/**
* If the body of a for loop or the then clause of an if statement has
* a single statement, should it be wrapped in a block?
* {@inheritDoc}
*/
@Override
boolean shouldPreserveExtraBlocks() {
// When pretty-printing, always place the statement in its own block
// so it is printed on a separate line. This allows breakpoints to be
// placed on the statement.
return true;
}
/**
* @return The TRY node for the specified CATCH node.
*/
private Node getTryForCatch(Node n) {
return n.getParent().getParent();
}
/**
* @return Whether the a line break should be added after the specified
* BLOCK.
*/
@Override
boolean breakAfterBlockFor(Node n, boolean isStatementContext) {
Preconditions.checkState(n.isBlock());
Node parent = n.getParent
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> automatically.
*/
Builder setLineBreak(boolean lineBreak) {
this.lineBreak = lineBreak;
return this;
}
/**
* Sets whether line breaking is preferred at end of file. This is useful
* if JS serving code needs a place to insert code, such as script tags,
* without interfering with source maps.
* @param lineBreakAtEnd If true, prefer line breaking at end of file.
*/
Builder setPreferLineBreakAtEndOfFile(boolean lineBreakAtEnd) {
this.preferLineBreakAtEndOfFile = lineBreakAtEnd;
return this;
}
/**
* Sets whether to output closure-style type annotations.
* @param outputTypes If true, outputs closure-style type annotations.
*/
Builder setOutputTypes(boolean outputTypes) {
this.outputTypes = outputTypes;
return this;
}
/**
* Sets the line length threshold that will be used to determine
* when to break lines, if line breaking is on.
*
* @param threshold The line length threshold.
*/
Builder setLineLengthThreshold(int threshold) {
this.lineLengthThreshold = threshold;
return this;
}
/**
* Sets the source map to which to write the metadata about
* the generated source code.
*
* @param sourceMap The source map.
*/
Builder setSourceMap(SourceMap sourceMap) {
this.sourceMap = sourceMap;
return this;
}
/**
* @param level The detail level to use.
*/
Builder setSourceMapDetailLevel(SourceMap.DetailLevel level) {
Preconditions.checkState(level != null);
this.sourceMapDetailLevel = level;
return this;
}
/**
* Set the charset to use when determining what characters need to be
* escaped in the output.
*/
Builder setOutputCharset(Charset outCharset) {
this.outputCharset = outCharset;
return this;
}
/**
* Set whether the output should be tags as ECMASCRIPT 5 Strict.
*/
Builder setTagAsStrict(boolean tagAsStrict) {
this.tagAsStrict = tagAsStrict;
return this;
}
/**
* Generates the source code and returns it.
*/
String build() {
if (root == null
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) {
throw new IllegalStateException(
"Cannot build without root node being specified");
}
Format outputFormat = outputTypes
? Format.TYPED
: prettyPrint
? Format.PRETTY
: Format.COMPACT;
return toSource(root, outputFormat, lineBreak, preferLineBreakAtEndOfFile,
lineLengthThreshold, sourceMap, sourceMapDetailLevel, outputCharset,
tagAsStrict);
}
}
enum Format {
COMPACT,
PRETTY,
TYPED
}
/**
* Converts a tree to JS code
*/
private static String toSource(Node root, Format outputFormat,
boolean lineBreak, boolean preferEndOfFileBreak,
int lineLengthThreshold,
SourceMap sourceMap,
SourceMap.DetailLevel sourceMapDetailLevel,
Charset outputCharset,
boolean tagAsStrict) {
Preconditions.checkState(sourceMapDetailLevel != null);
boolean createSourceMap = (sourceMap != null);
MappedCodePrinter mcp =
outputFormat == Format.COMPACT
? new CompactCodePrinter(
lineBreak, preferEndOfFileBreak, lineLengthThreshold,
createSourceMap, sourceMapDetailLevel)
: new PrettyCodePrinter(
lineLengthThreshold, createSourceMap, sourceMapDetailLevel);
CodeGenerator cg =
outputFormat == Format.TYPED
? new TypedCodeGenerator(mcp, outputCharset)
: new CodeGenerator(mcp, outputCharset);
if (tagAsStrict) {
cg.tagAsStrict();
}
cg.add(root);
mcp.endFile();
String code = mcp.getCode();
if (createSourceMap) {
mcp.generateSourceMap(sourceMap);
}
return code;
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> the $ must be
* upper case.
* $A Constant - doesn't have to be anything in front of the $
* </pre>
*/
@Override
public boolean isConstant(String name) {
if (name.length() <= 1) {
return false;
}
// In compiled code, '$' is often a namespace delimiter. To allow inlining
// of namespaced constants, we strip off any namespaces here.
int pos = name.lastIndexOf('$');
if (pos >= 0) {
name = name.substring(pos + 1);
if (name.length() == 0) {
return false;
}
}
return isConstantKey(name);
}
@Override
public boolean isConstantKey(String name) {
if (name.isEmpty() || !Character.isUpperCase(name.charAt(0))) {
return false;
}
// hack way of checking that there aren't any lower-case letters
return name.toUpperCase().equals(name);
}
/**
* {@inheritDoc}
*
* <p>This enforces Google's convention about enum key names. They must match
* the regular expression {@code [A-Z0-9][A-Z0-9_]*}.
*
* <p>Examples:
* <ul>
* <li>A</li>
* <li>213</li>
* <li>FOO_BAR</li>
* </ul>
*/
@Override
public boolean isValidEnumKey(String key) {
return ENUM_KEY_PATTERN.matcher(key).matches();
}
/**
* {@inheritDoc}
*
* <p>In Google code, parameter names beginning with {@code opt_} are
* treated as optional arguments.
*/
@Override
public boolean isOptionalParameter(Node parameter) {
return parameter.getString().startsWith(OPTIONAL_ARG_PREFIX);
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return VAR_ARGS_NAME.equals(parameter.getString());
}
/**
* {@inheritDoc}
*
* <p>In Google code, any global name starting with an underscore is
* considered exported.
*/
@Override
public boolean isExported(String name, boolean local) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> return super.isExported(name, local) ||
(!local && name.startsWith("_"));
}
/**
* {@inheritDoc}
*
* <p>In Google code, private names end with an underscore, and exported
* names are never considered private (see {@link #isExported}).
*/
@Override
public boolean isPrivate(String name) {
return name.endsWith("_") && !isExported(name);
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS><Var> getReferences(Var var) {
return ImmutableList.of(var);
}
@Override
public Scope getScope(Var var) {
return var.scope;
}
@Override
public Iterable<Var> getAllSymbols() {
List<Var> vars = Lists.newArrayList();
for (Scope s : scopes.values()) {
Iterables.addAll(vars, s.getAllSymbols());
}
return vars;
}
@Override
public Scope createScope(Node n, Scope parent) {
Scope scope = scopes.get(n);
if (scope == null) {
scope = delegate.createScope(n, parent);
scopes.put(n, scope);
} else {
Preconditions.checkState(parent == scope.getParent());
}
return scope;
}
Collection<Scope> getAllMemoizedScopes() {
return Collections.unmodifiableCollection(scopes.values());
}
Scope getScopeIfMemoized(Node n) {
return scopes.get(n);
}
/**
* Removes all scopes with root nodes from a given script file.
*
* @param scriptName the name of the script file to remove nodes for.
*/
void removeScopesForScript(String scriptName) {
for (Node scopeRoot : ImmutableSet.copyOf(scopes.keySet())) {
if (scriptName.equals(scopeRoot.getSourceFileName())) {
scopes.remove(scopeRoot);
}
}
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>()) {
if (isCurrentLoopPopulated) {
passes.add(currentLoop);
currentLoop = new LoopInternal();
isCurrentLoopPopulated = false;
}
addOneTimePass(factory);
} else {
currentLoop.addLoopedPass(factory);
isCurrentLoopPopulated = true;
}
}
if (isCurrentLoopPopulated) {
passes.add(currentLoop);
}
}
/**
* Add the pass generated by the given factory to the compile sequence.
* This pass will be run once.
*/
void addOneTimePass(PassFactory factory) {
passes.add(new PassFactoryDelegate(compiler, factory));
}
/**
* Add a loop to the compile sequence. This loop will continue running
* until the AST stops changing.
* @return The loop structure. Pass suppliers should be added to the loop.
*/
Loop addFixedPointLoop() {
Loop loop = new LoopInternal();
passes.add(loop);
return loop;
}
/**
* Adds a sanity checker to be run after every pass. Intended for development.
*/
void setSanityCheck(PassFactory sanityCheck) {
this.sanityCheck = sanityCheck;
}
/**
* Run all the passes in the optimizer.
*/
@Override
public void process(Node externs, Node root) {
double progress = 0.0;
double progressStep = 0.0;
if (progressRange != null) {
progressStep = (progressRange.maxValue - progressRange.initialValue)
/ passes.size();
progress = progressRange.initialValue;
}
for (CompilerPass pass : passes) {
pass.process(externs, root);
if (progressRange != null) {
progress += progressStep;
compiler.setProgress(progress);
}
if (hasHaltingErrors()) {
return;
}
}
}
/**
* Marks the beginning of a pass.
*/
private void startPass(String passName) {
Preconditions.checkState(currentTracer == null && currentPassName == null);
currentPassName = passName;
currentTracer = newTracer(passName);
}
/**
* Marks the end of a pass.
*/
private void end
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Pass(Node externs, Node root) {
Preconditions.checkState(currentTracer != null && currentPassName != null);
String passToCheck = currentPassName;
try {
stopTracer(currentTracer, currentPassName);
currentPassName = null;
currentTracer = null;
maybeSanityCheck(externs, root);
} catch (Exception e) {
// TODO(johnlenz): Remove this once the normalization checks report
// errors instead of exceptions.
throw new RuntimeException("Sanity check failed for " + passToCheck, e);
}
}
/**
* Runs the sanity check if it is available.
*/
void maybeSanityCheck(Node externs, Node root) {
if (sanityCheck != null) {
sanityCheck.create(compiler).process(externs, root);
}
}
private boolean hasHaltingErrors() {
return compiler.hasHaltingErrors();
}
/**
* Returns a new tracer for the given pass name.
*/
private Tracer newTracer(String passName) {
String comment = passName +
(recentChange.hasCodeChanged() ? " on recently changed AST" : "");
if (tracker != null) {
tracker.recordPassStart(passName);
}
return new Tracer("JSCompiler", comment);
}
private void stopTracer(Tracer t, String passName) {
long result = t.stop();
if (tracker != null) {
tracker.recordPassStop(passName, result);
}
}
/**
* A single compiler pass.
*/
private abstract class NamedPass implements CompilerPass {
private final String name;
NamedPass(String name) {
this.name = name;
}
@Override
public void process(Node externs, Node root) {
logger.fine(name);
startPass(name);
processInternal(externs, root);
endPass(externs, root);
}
abstract void processInternal(Node externs, Node root);
}
/**
* Delegates to a PassFactory for processing.
*/
private class PassFactoryDelegate extends NamedPass {
private final AbstractCompiler myCompiler;
private final PassFactory factory;
private PassFactoryDelegate(
AbstractCompiler myCompiler, PassFactory factory) {
super(factory.getName());
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this.myCompiler = myCompiler;
this.factory = factory;
}
@Override
void processInternal(Node externs, Node root) {
factory.create(myCompiler).process(externs, root);
}
}
/**
* Runs a set of compiler passes until they reach a fixed point.
*/
static abstract class Loop implements CompilerPass {
abstract void addLoopedPass(PassFactory factory);
}
/**
* Runs a set of compiler passes until they reach a fixed point.
*
* Notice that this is a non-static class, because it includes the closure
* of PhaseOptimizer.
*/
private class LoopInternal extends Loop {
private final List<NamedPass> myPasses = Lists.newArrayList();
private final Set<String> myNames = Sets.newHashSet();
@Override
void addLoopedPass(PassFactory factory) {
String name = factory.getName();
Preconditions.checkArgument(!myNames.contains(name),
"Already a pass with name '%s' in this loop", name);
myNames.add(factory.getName());
myPasses.add(new PassFactoryDelegate(compiler, factory));
}
/**
* Gets the pass names, in order.
*/
private List<String> getPassOrder() {
List<String> order = Lists.newArrayList();
for (NamedPass pass : myPasses) {
order.add(pass.name);
}
return order;
}
@Override
public void process(Node externs, Node root) {
Preconditions.checkState(!loopMutex, "Nested loops are forbidden");
loopMutex = true;
if (randomizeLoops) {
randomizePasses();
} else {
optimizePasses();
}
try {
// TODO(nicksantos): Use a smarter algorithm that dynamically adjusts
// the order that passes are run in.
int count = 0;
out: do {
if (count++ > MAX_LOOPS) {
compiler.throwInternalError(OPTIMIZE_LOOP_ERROR, null);
}
recentChange.reset(); // reset before this round of optimizations
for (CompilerPass pass : myPasses) {
pass.process(externs, root);
if (hasHaltingErrors()) {
break out;
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> true;
}
@Override
public Collection<GraphNode<N, E>> getNodes() {
return Collections.<GraphNode<N, E>>unmodifiableCollection(nodes.values());
}
@Override
public List<GraphNode<N, E>> getNeighborNodes(N value) {
DiGraphNode<N, E> node = getDirectedGraphNode(value);
return getNeighborNodes(node);
}
public List<GraphNode<N, E>> getNeighborNodes(DiGraphNode<N, E> node) {
List<GraphNode<N, E>> result = Lists.newArrayList();
for (Iterator<GraphNode<N, E>> i =
((LinkedDirectedGraphNode<N, E>) node).neighborIterator();i.hasNext();) {
result.add(i.next());
}
return result;
}
@Override
public Iterator<GraphNode<N, E>> getNeighborNodesIterator(N value) {
LinkedDirectedGraphNode<N, E> node = nodes.get(value);
Preconditions.checkNotNull(node);
return node.neighborIterator();
}
@Override
public List<GraphEdge<N, E>> getEdges() {
List<GraphEdge<N, E>> result = Lists.newArrayList();
for (DiGraphNode<N, E> node : nodes.values()) {
for (DiGraphEdge<N, E> edge : node.getOutEdges()) {
result.add(edge);
}
}
return Collections.unmodifiableList(result);
}
@Override
public int getNodeDegree(N value) {
DiGraphNode<N, E> node = getNodeOrFail(value);
return node.getInEdges().size() + node.getOutEdges().size();
}
/**
* A directed graph node that stores outgoing edges and incoming edges as an
* list within the node itself.
*/
static class LinkedDirectedGraphNode<N, E> implements DiGraphNode<N, E>,
GraphvizNode {
List<DiGraphEdge<N, E>> inEdgeList = Lists.newArrayList();
List<DiGraphEdge<N, E>> outEdgeList =
Lists.newArrayList();
protected final N value;
/**
* Constructor
*
* @param nodeValue Node's value.
*/
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Prepare the AST before we do any checks or optimizations on it.
*
* This pass must run. It should bring the AST into a consistent state,
* and add annotations where necessary. It should not make any transformations
* on the tree that would lose source information, since we need that source
* information for checks.
*
* @author johnlenz@google.com (John Lenz)
*/
class PrepareAst implements CompilerPass {
private final AbstractCompiler compiler;
private final boolean checkOnly;
PrepareAst(AbstractCompiler compiler) {
this(compiler, false);
}
PrepareAst(AbstractCompiler compiler, boolean checkOnly) {
this.compiler = compiler;
this.checkOnly = checkOnly;
}
private void reportChange() {
if (checkOnly) {
Preconditions.checkState(false, "normalizeNodeType constraints violated");
}
}
@Override
public void process(Node externs, Node root) {
if (checkOnly) {
normalizeNodeTypes(root);
} else {
// Don't perform "PrepareAnnotations" when doing checks as
// they currently aren't valid during sanity checks. In particular,
// they DIRECT_EVAL shouldn't be
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> applied after inlining has been
// performed.
if (externs != null) {
NodeTraversal.traverse(
compiler, externs, new PrepareAnnotations(compiler));
}
if (root != null) {
NodeTraversal.traverse(
compiler, root, new PrepareAnnotations(compiler));
}
}
}
/**
* Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& !n.isLabel()
&& !n.isSwitch()) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n,c) &&
!c.isBlock()) {
Node newBlock = IR.block().srcref(n);
n.replaceChild(c, newBlock);
if (!c.isEmpty()) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
implements NodeTraversal.Callback {
private final CodingConvention convention;
PrepareAnnotations(AbstractCompiler compiler) {
this.convention = compiler.getCodingConvention();
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isObjectLit()) {
normalizeObjectLiteralAnnotations(n);
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.getType()) {
case Token.CALL:
annotateCalls(n);
break;
case Token.FUNCTION:
annotateFunctions(n, parent);
annotateDispatchers(n, parent);
break;
}
}
private void normalizeObjectLiteralAnnotations(Node objlit) {
Preconditions.checkState(objlit.isObjectLit());
for (Node key = objlit.getFirstChild();
key != null; key = key.getNext()) {
Node value = key.getFirstChild();
normalizeObjectLiteralKeyAnnotations(objlit, key, value);
}
}
/**
* There are two types of calls we are interested in calls without explicit
* "this" values (what we are call "free" calls) and direct call to eval.
*/
private void annotateCalls(Node n) {
Preconditions.checkState(n.isCall());
// Keep track of of the "this" context of a call. A call without an
// explicit "this" is a free call.
Node first = n.getFirstChild();
if (!NodeUtil.isGet(first)) {
n.putBooleanProp(Node.FREE_CALL, true);
}
// Keep track of the context in which eval is called. It is important
// to distinguish between "(0, eval)()" and "eval()".
if (first.isName() &&
"eval".equals(first.getString())) {
first.putBooleanProp(Node.DIRECT_EVAL, true);
}
}
/**
* Translate dispatcher info into the property expected node.
*/
private void annotateDispatchers(Node n, Node parent) {
Preconditions.checkState(n.isFunction());
if (parent.getJSDocInfo() != null
&& parent.getJSDocInfo().isJavaDispatch()) {
if (parent.isAssign()) {
Preconditions.checkState(parent.getLastChild() == n);
n.putBooleanProp(Node.IS_DISPATCHER, true);
}
}
}
/**
* In the AST that Rhino gives us, it needs to make a distinction
* between JsDoc on the object literal node and JsDoc on the object literal
* value. For example,
* <pre>
* var x = {
* / JSDOC /
* a: 'b
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>',
* c: / JSDOC / 'd'
* };
* </pre>
*
* But in few narrow cases (in particular, function literals), it's
* a lot easier for us if the doc is attached to the value.
*/
private void normalizeObjectLiteralKeyAnnotations(
Node objlit, Node key, Node value) {
Preconditions.checkState(objlit.isObjectLit());
if (key.getJSDocInfo() != null &&
value.isFunction()) {
value.setJSDocInfo(key.getJSDocInfo());
}
}
/**
* Annotate optional and var_arg function parameters.
*/
private void annotateFunctions(Node n, Node parent) {
JSDocInfo fnInfo = NodeUtil.getFunctionJSDocInfo(n);
// Compute which function parameters are optional and
// which are var_args.
Node args = n.getFirstChild().getNext();
for (Node arg = args.getFirstChild();
arg != null;
arg = arg.getNext()) {
String argName = arg.getString();
JSTypeExpression typeExpr = fnInfo == null ?
null : fnInfo.getParameterType(argName);
if (convention.isOptionalParameter(arg) ||
typeExpr != null && typeExpr.isOptionalArg()) {
arg.putBooleanProp(Node.IS_OPTIONAL_PARAM, true);
}
if (convention.isVarArgsParameter(arg) ||
typeExpr != null && typeExpr.isVarArgs()) {
arg.putBooleanProp(Node.IS_VAR_ARGS_PARAM, true);
}
}
}
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> do this inference globally.
if (n.isGetProp() &&
!t.inGlobalScope() && type.isNullType()) {
return true;
}
mismatch(t, n, msg, type, expectedType);
return false;
}
return true;
}
private boolean containsForwardDeclaredUnresolvedName(JSType type) {
if (type.isUnionType()) {
for (JSType alt : type.toMaybeUnionType().getAlternates()) {
if (containsForwardDeclaredUnresolvedName(alt)) {
return true;
}
}
}
return type.isNoResolvedType();
}
/**
* Expect that the type of a switch condition matches the type of its
* case condition.
*/
void expectSwitchMatchesCase(NodeTraversal t, Node n, JSType switchType,
JSType caseType) {
// ECMA-262, page 68, step 3 of evaluation of CaseBlock,
// but allowing extra autoboxing.
// TODO(user): remove extra conditions when type annotations
// in the code base have adapted to the change in the compiler.
if (!switchType.canTestForShallowEqualityWith(caseType) &&
(caseType.autoboxesTo() == null ||
!caseType.autoboxesTo().isSubtype(switchType))) {
mismatch(t, n.getFirstChild(),
"case expression doesn't match switch",
caseType, switchType);
}
}
/**
* Expect that the first type can be addressed with GETELEM syntax,
* and that the second type is the right type for an index into the
* first type.
*
* @param t The node traversal.
* @param n The GETELEM node to issue warnings on.
* @param objType The type of the left side of the GETELEM.
* @param indexType The type inside the brackets of the GETELEM.
*/
void expectIndexMatch(NodeTraversal t, Node n, JSType objType,
JSType indexType) {
Preconditions.checkState(n.isGetElem());
Node indexNode = n.getLastChild();
if (objType.isUnknownType()) {
expectStringOrNumber(t, indexNode, indexType, "property access");
} else {
ObjectType dereferenced =
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Other duplicates
// will be reported by the syntactic scope creator later in the
// compilation process.
if (varType != null &&
varType != typeRegistry.getNativeType(UNKNOWN_TYPE) &&
newType != null &&
newType != typeRegistry.getNativeType(UNKNOWN_TYPE)) {
// If there are two typed declarations of the same variable, that
// is an error and the second declaration is ignored, except in the
// case of native types. A null input type means that the declaration
// was made in TypedScopeCreator#createInitialScope and is a
// native type. We should redeclare it at the new input site.
if (var.input == null) {
Scope s = var.getScope();
s.undeclare(var);
newVar = s.declare(variableName, n, varType, input, false);
n.setJSType(varType);
if (parent.isVar()) {
if (n.getFirstChild() != null) {
n.getFirstChild().setJSType(varType);
}
} else {
Preconditions.checkState(parent.isFunction());
parent.setJSType(varType);
}
} else {
// Always warn about duplicates if the overridden type does not
// match the original type.
//
// If the types match, suppress the warning iff there was a @suppress
// tag, or if the original declaration was a stub.
if (!(allowDupe ||
var.getParentNode().isExprResult()) ||
!newType.equals(varType)) {
report(JSError.make(sourceName, n, DUP_VAR_DECLARATION,
variableName, newType.toString(), var.getInputName(),
String.valueOf(var.nameNode.getLineno()),
varType.toString()));
}
}
}
return newVar;
}
/**
* Expect that all properties on interfaces that this type implements are
* implemented and correctly typed.
*/
void expectAllInterfaceProperties(NodeTraversal t, Node n,
FunctionType type) {
ObjectType instance = type.getInstanceType();
for (ObjectType implemented : type.getAllImplementedInterfaces()) {
if (implemented.getImplicitPrototype() != null) {
for (String prop :
implemented.getImplicitPrototype().getOwnPropertyNames()) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.parsing;
import com.google.common.base.Preconditions;
import com.google.javascript.rhino.head.ScriptRuntime;
/**
* This class implements the scanner for JsDoc strings.
*
* It is heavily based on Rhino's TokenStream.
*
*/
class JsDocTokenStream {
/*
* For chars - because we need something out-of-range
* to check. (And checking EOF by exception is annoying.)
* Note distinction from EOF token type!
*/
private final static int
EOF_CHAR = -1;
JsDocTokenStream(String sourceString) {
this(sourceString, 0);
}
JsDocTokenStream(String sourceString, int lineno) {
this(sourceString, lineno, 0);
}
JsDocTokenStream(String sourceString, int lineno, int initCharno) {
Preconditions.checkNotNull(sourceString);
this.lineno = lineno;
this.sourceString = sourceString;
this.sourceEnd = sourceString.length();
this.sourceCursor = this.cursor = 0;
this.initLineno = lineno;
this.initCharno = initCharno;
}
/**
* Tokenizes JSDoc comments.
*/
@SuppressWarnings("fallthrough")
final JsDocToken getJsDocToken() {
int c;
stringBufferTop = 0;
for (;;) {
// eat white spaces
for (;;) {
charno = -1;
c = getChar();
if (c == EOF_CHAR) {
return JsDocToken
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.graph.LatticeElement;
import java.util.List;
/**
* Defines a way join a list of LatticeElements.
*/
interface JoinOp<L extends LatticeElement> extends Function<List<L>, L> {
/**
* An implementation of {@code JoinOp} that makes it easy to join to
* lattice elements at a time.
*/
static abstract class BinaryJoinOp<L extends LatticeElement>
implements JoinOp<L> {
@Override
public final L apply(List<L> values) {
Preconditions.checkArgument(!values.isEmpty());
int size = values.size();
if (size == 1) {
return values.get(0);
} else if (size == 2) {
return apply(values.get(0), values.get(1));
} else {
int mid = computeMidPoint(size);
return apply(
apply(values.subList(0, mid)),
apply(values.subList(mid, size)));
}
}
/**
* Creates a new lattice that will be the join of two input lattices.
*
* @return The join of {@code latticeA} and {@code latticeB}.
*/
abstract L apply(L latticeA, L latticeB);
/**
* Finds the midpoint of a list. The function will favor two lists of
* even length instead of two lists of the same odd length.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.makeError(val, INVALID_DEFINE_INIT_ERROR, fullName));
} else if (processDefineAssignment(t, fullName, val, valParent)) {
// remove the assignment so that the variable is still declared,
// but no longer assigned to a value, e.g.,
// DEF_FOO = 5; // becomes "5;"
// We can't remove the ASSIGN/VAR when we're still visiting its
// children, so we'll have to come back later to remove it.
refInfo.name.removeRef(ref);
lvalueToRemoveLater = valParent;
}
break;
default:
if (t.inGlobalScope()) {
// Treat this as a reference to a define in the global scope.
// After this point, the define must not be reassigned,
// or it's an error.
DefineInfo info = assignableDefines.get(fullName);
if (info != null) {
setDefineInfoNotAssignable(info, t);
assignableDefines.remove(fullName);
}
}
break;
}
}
if (!t.inGlobalScope() &&
n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) {
// warn about @define annotations in local scopes
compiler.report(
t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, ""));
}
if (lvalueToRemoveLater == n) {
lvalueToRemoveLater = null;
if (n.isAssign()) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.checkState(n.isName());
n.removeChild(n.getFirstChild());
}
compiler.reportCodeChange();
}
if (n.isCall()) {
if (t.inGlobalScope()) {
// If there's a function call in the global scope,
// we just say it's unsafe and freeze all the defines.
//
// NOTE(nicksantos): We could be a lot smarter here. For example,
// ReplaceOverriddenVars keeps a call graph of all functions and
// which functions/variables that they reference, and tries
// to statically determine which functions are "safe" and which
// are
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = getNativeType(JSTypeNative.UNKNOWN_TYPE);
name.setJSType(type);
redeclareSimpleVar(scope, name, type);
return scope;
}
private FlowScope traverseAssign(Node n, FlowScope scope) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
scope = traverseChildren(n, scope);
JSType leftType = left.getJSType();
JSType rightType = getJSType(right);
n.setJSType(rightType);
updateScopeForTypeChange(scope, left, leftType, rightType);
return scope;
}
/**
* Updates the scope according to the result of a type change, like
* an assignment or a type cast.
*/
private void updateScopeForTypeChange(
FlowScope scope, Node left, JSType leftType, JSType resultType) {
Preconditions.checkNotNull(resultType);
switch (left.getType()) {
case Token.NAME:
String varName = left.getString();
Var var = syntacticScope.getVar(varName);
// When looking at VAR initializers for declared VARs, we trust
// the declared type over the type it's being initialized to.
// This has two purposes:
// 1) We avoid re-declaring declared variables so that built-in
// types defined in externs are not redeclared.
// 2) When there's a lexical closure like
// /** @type {?string} */ var x = null;
// function f() { x = 'xyz'; }
// the inference will ignore the lexical closure,
// which is just wrong. This bug needs to be fixed eventually.
boolean isVarDeclaration = left.hasChildren();
if (!isVarDeclaration || var == null || var.isTypeInferred()) {
redeclareSimpleVar(scope, left, resultType);
}
left.setJSType(isVarDeclaration || leftType == null ?
resultType : null);
if (var != null && var.isTypeInferred()) {
JSType oldType = var.getType();
var.setType(oldType == null ?
resultType : oldType.getLeastSupertype(resultType));
}
break;
case Token.GETPROP:
String qualifiedName = left.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> we have it.
// 1) The var is escaped in a weird way, e.g.,
// function f() { var x = 3; function g() { x = null } (x); }
boolean isInferred = var.isTypeInferred();
boolean unflowable = isInferred &&
isUnflowable(syntacticScope.getVar(varName));
// 2) We're reading type information from another scope for an
// inferred variable.
// var t = null; function f() { (t); }
boolean nonLocalInferredSlot =
isInferred &&
syntacticScope.getParent() != null &&
var == syntacticScope.getParent().getSlot(varName);
if (!unflowable && !nonLocalInferredSlot) {
type = var.getType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
}
}
}
}
n.setJSType(type);
return scope;
}
/** Traverse each element of the array. */
private FlowScope traverseArrayLiteral(Node n, FlowScope scope) {
scope = traverseChildren(n, scope);
n.setJSType(getNativeType(ARRAY_TYPE));
return scope;
}
private FlowScope traverseObjectLiteral(Node n, FlowScope scope) {
JSType type = n.getJSType();
Preconditions.checkNotNull(type);
for (Node name = n.getFirstChild(); name != null; name = name.getNext()) {
scope = traverse(name.getFirstChild(), scope);
}
// Object literals can be reflected on other types, or changed with
// type casts.
// See CodingConvention#getObjectLiteralCase and goog.object.reflect.
// Ignore these types of literals.
// TODO(nicksantos): There should be an "anonymous object" type that
// we can check for here.
ObjectType objectType = ObjectType.cast(type);
if (objectType == null) {
return scope;
}
boolean hasLendsName = n.getJSDocInfo() != null &&
n.getJSDocInfo().getLendsName() != null;
if (objectType.hasReferenceName() && !hasLendsName) {
return scope;
}
String qObjName = NodeUtil.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>}
resolvedTemplateType(
resolvedTypes, paramType.toMaybeTemplateType(), argType);
} else if (paramType.isUnionType()) {
// @param {Array.<T>|NodeList|Arguments|{length:number}}
UnionType unionType = paramType.toMaybeUnionType();
for (JSType alernative : unionType.getAlternates()) {
maybeResolveTemplatedType(alernative, argType, resolvedTypes);
}
} else if (paramType.isFunctionType()) {
FunctionType paramFunctionType = paramType.toMaybeFunctionType();
FunctionType argFunctionType = argType
.restrictByNotNullOrUndefined()
.collapseUnion()
.toMaybeFunctionType();
if (argFunctionType != null && argFunctionType.isSubtype(paramType)) {
// infer from return type of the function type
maybeResolveTemplatedType(
paramFunctionType.getReturnType(),
argFunctionType.getReturnType(), resolvedTypes);
// infer from parameter types of the function type
maybeResolveTemplateTypeFromNodes(
paramFunctionType.getParameters(),
argFunctionType.getParameters(), resolvedTypes);
}
} else if (paramType.isParameterizedType()) {
ParameterizedType paramObjectType = paramType.toMaybeParameterizedType();
JSType typeParameter = paramObjectType.getParameterType();
Preconditions.checkNotNull(typeParameter);
if (typeParameter != null) {
// @param {Array.<T>}
ObjectType argObjectType = argType
.restrictByNotNullOrUndefined()
.collapseUnion()
.toMaybeParameterizedType();
if (argObjectType != null && argObjectType.isSubtype(paramType)) {
JSType argTypeParameter = argObjectType.getParameterType();
Preconditions.checkNotNull(argTypeParameter);
maybeResolveTemplatedType(
typeParameter, argTypeParameter, resolvedTypes);
}
}
}
}
private void maybeResolveTemplateTypeFromNodes(
Iterable<Node> declParams,
Iterable<Node> callParams,
Map<TemplateType, JSType> resolvedTypes) {
maybeResolveTemplateTypeFromNodes(
declParams.iterator(), callParams.iterator(), resolvedTypes);
}
private void maybeResolveTemplateTypeFromNodes(
Iterator<Node> declParams,
Iterator<Node> callParams,
Map<TemplateType, JSType> resolvedTypes) {
while (declParams.hasNext() && callParams
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.hasNext()) {
maybeResolveTemplatedType(
getJSType(declParams.next()),
getJSType(callParams.next()),
resolvedTypes);
}
}
private void resolvedTemplateType(
Map<TemplateType, JSType> map, TemplateType template, JSType resolved) {
JSType previous = map.get(template);
if (!resolved.isUnknownType()) {
if (previous == null) {
map.put(template, resolved);
} else {
JSType join = previous.getLeastSupertype(resolved);
map.put(template, join);
}
}
}
private static class TemplateTypeReplacer extends ModificationVisitor {
private final Map<TemplateType, JSType> replacements;
private final JSTypeRegistry registry;
TemplateTypeReplacer(
JSTypeRegistry registry, Map<TemplateType, JSType> replacements) {
super(registry);
this.registry = registry;
this.replacements = replacements;
}
@Override
public JSType caseTemplateType(TemplateType type) {
JSType replacement = replacements.get(type);
return replacement != null ?
replacement : registry.getNativeType(UNKNOWN_TYPE);
}
}
/**
* For functions with function(this: T, ...) and T as parameters, type
* inference will set the type of this on a function literal argument to the
* the actual type of T.
*/
private boolean inferTemplatedTypesForCall(
Node n, FunctionType fnType) {
if (fnType.getTemplateTypeNames().isEmpty()) {
return false;
}
// Try to infer the template types
Map<TemplateType, JSType> inferred = inferTemplateTypesFromParameters(
fnType, n);
if (inferred.size() > 0) {
// Something useful was found, try to replace it.
TemplateTypeReplacer replacer = new TemplateTypeReplacer(
registry, inferred);
Node callTarget = n.getFirstChild();
FunctionType replacementFnType = fnType.visit(replacer)
.toMaybeFunctionType();
Preconditions.checkNotNull(replacementFnType);
callTarget.setJSType(replacementFnType);
n.setJSType(replacementFnType.getReturnType());
return true;
}
return false;
}
private
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = rightScope;
}
/**
* Gets the safe estimated scope without knowing if all of the
* subexpressions will be evaluated.
*/
FlowScope getJoinedFlowScope() {
if (joinedScope == null) {
if (leftScope == rightScope) {
joinedScope = rightScope;
} else {
joinedScope = join(leftScope, rightScope);
}
}
return joinedScope;
}
/**
* Gets the outcome scope if we do know the outcome of the entire
* expression.
*/
FlowScope getOutcomeFlowScope(int nodeType, boolean outcome) {
if (nodeType == Token.AND && outcome ||
nodeType == Token.OR && !outcome) {
// We know that the whole expression must have executed.
return rightScope;
} else {
return getJoinedFlowScope();
}
}
}
private BooleanOutcomePair newBooleanOutcomePair(
JSType jsType, FlowScope flowScope) {
if (jsType == null) {
return new BooleanOutcomePair(
BooleanLiteralSet.BOTH, BooleanLiteralSet.BOTH, flowScope, flowScope);
}
return new BooleanOutcomePair(jsType.getPossibleToBooleanOutcomes(),
registry.getNativeType(BOOLEAN_TYPE).isSubtype(jsType) ?
BooleanLiteralSet.BOTH : BooleanLiteralSet.EMPTY,
flowScope, flowScope);
}
private void redeclareSimpleVar(
FlowScope scope, Node nameNode, JSType varType) {
Preconditions.checkState(nameNode.isName());
String varName = nameNode.getString();
if (varType == null) {
varType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
if (isUnflowable(syntacticScope.getVar(varName))) {
return;
}
scope.inferSlotType(varName, varType);
}
private boolean isUnflowable(Var v) {
return v != null && v.isLocal() && v.isMarkedEscaped() &&
// It's OK to flow a variable in the scope where it's escaped.
v.getScope() == syntacticScope;
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) {
traverseRoots(Lists.newArrayList(roots));
}
public void traverseRoots(List<Node> roots) {
if (roots.isEmpty()) {
return;
}
try {
Node scopeRoot = roots.get(0).getParent();
Preconditions.checkState(scopeRoot != null);
inputId = NodeUtil.getInputId(scopeRoot);
sourceName = "";
curNode = scopeRoot;
pushScope(scopeRoot);
for (Node root : roots) {
Preconditions.checkState(root.getParent() == scopeRoot);
traverseBranch(root, scopeRoot);
}
popScope();
} catch (Exception unexpectedException) {
throwUnexpectedException(unexpectedException);
}
}
private static final String MISSING_SOURCE = "[source unknown]";
private String formatNodePosition(Node n) {
if (n == null) {
return MISSING_SOURCE + "\n";
}
int lineNumber = n.getLineno();
int columnNumber = n.getCharno();
String src = compiler.getSourceLine(sourceName, lineNumber);
if (src == null) {
src = MISSING_SOURCE;
}
return sourceName + ":" + lineNumber + ":" + columnNumber + "\n"
+ src + "\n";
}
/**
* Traverses a parse tree recursively with a scope, starting with the given
* root. This should only be used in the global scope. Otherwise, use
* {@link #traverseAtScope}.
*/
void traverseWithScope(Node root, Scope s) {
Preconditions.checkState(s.isGlobal());
inputId = null;
sourceName = "";
curNode = root;
pushScope(s);
traverseBranch(root, null);
popScope();
}
/**
* Traverses a parse tree recursively with a scope, starting at that scope's
* root.
*/
void traverseAtScope(Scope s) {
Node n = s.getRootNode();
if (n.isFunction()) {
// We need to do some extra magic to make sure that the scope doesn't
// get re-created when we dive into the function.
if (inputId == null) {
inputId = NodeUtil.getInputId(n);
}
sourceName = getSourceName(n);
cur
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node = n;
pushScope(s);
Node args = n.getFirstChild().getNext();
Node body = args.getNext();
traverseBranch(args, n);
traverseBranch(body, n);
popScope();
} else {
traverseWithScope(n, s);
}
}
/**
* Traverses an inner node recursively with a refined scope. An inner node may
* be any node with a non {@code null} parent (i.e. all nodes except the
* root).
*
* @param node the node to traverse
* @param parent the node's parent, it may not be {@code null}
* @param refinedScope the refined scope of the scope currently at the top of
* the scope stack or in trivial cases that very scope or {@code null}
*/
protected void traverseInnerNode(Node node, Node parent, Scope refinedScope) {
Preconditions.checkNotNull(parent);
if (refinedScope != null && getScope() != refinedScope) {
curNode = node;
pushScope(refinedScope);
traverseBranch(node, parent);
popScope();
} else {
traverseBranch(node, parent);
}
}
/**
* Gets the compiler.
*/
public Compiler getCompiler() {
// TODO(nicksantos): Remove this type cast. This is just temporary
// while refactoring.
return (Compiler) compiler;
}
/**
* Gets the current line number, or zero if it cannot be determined. The line
* number is retrieved lazily as a running time optimization.
*/
public int getLineNumber() {
Node cur = curNode;
while (cur != null) {
int line = cur.getLineno();
if (line >=0) {
return line;
}
cur = cur.getParent();
}
return 0;
}
/**
* Gets the current input source name.
*
* @return A string that may be empty, but not null
*/
public String getSourceName() {
return sourceName;
}
/**
* Gets the current input source.
*/
public CompilerInput getInput() {
return compiler.getInput(inputId);
}
/**
* Gets the current input module.
*/
public JSModule getModule() {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
CompilerInput input = getInput();
return input == null ? null : input.getModule();
}
/** Returns the node currently being traversed. */
public Node getCurrentNode() {
return curNode;
}
/**
* Traverses a node recursively.
*/
public static void traverse(
AbstractCompiler compiler, Node root, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverse(root);
}
/**
* Traverses a list of node trees.
*/
public static void traverseRoots(
AbstractCompiler compiler, List<Node> roots, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
public static void traverseRoots(
AbstractCompiler compiler, Callback cb, Node ... roots) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
/**
* Traverses a branch.
*/
@SuppressWarnings("fallthrough")
private void traverseBranch(Node n, Node parent) {
int type = n.getType();
if (type == Token.SCRIPT) {
inputId = n.getInputId();
sourceName = getSourceName(n);
}
curNode = n;
if (!callback.shouldTraverse(this, n, parent)) return;
switch (type) {
case Token.FUNCTION:
traverseFunction(n, parent);
break;
default:
for (Node child = n.getFirstChild(); child != null; ) {
// child could be replaced, in which case our child node
// would no longer point to the true next
Node next = child.getNext();
traverseBranch(child, n);
child = next;
}
break;
}
curNode = n;
callback.visit(this, n, parent);
}
/**
* Traverses a function.
*/
private void traverseFunction(Node n, Node parent) {
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.isFunction());
final Node fnName = n.getFirstChild();
boolean isFunctionExpression = (parent != null)
&& NodeUtil.isFunctionExpression(n);
if (!isFunctionExpression) {
// Functions declarations are in the scope containing the
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> declaration.
traverseBranch(fnName, n);
}
curNode = n;
pushScope(n);
if (isFunctionExpression) {
// Function expression names are only accessible within the function
// scope.
traverseBranch(fnName, n);
}
final Node args = fnName.getNext();
final Node body = args.getNext();
// Args
traverseBranch(args, n);
// Body
Preconditions.checkState(body.getNext() == null &&
body.isBlock(), body);
traverseBranch(body, n);
popScope();
}
/** Examines the functions stack for the last instance of a function node. */
@SuppressWarnings("unchecked")
public Node getEnclosingFunction() {
if (scopes.size() + scopeRoots.size() < 2) {
return null;
} else {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Node node) {
Preconditions.checkState(curNode != null);
scopeRoots.push(node);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Scope s) {
Preconditions.checkState(curNode != null);
scopes.push(s);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Pops back to the previous scope (e.g. when leaving a function). */
private void popScope() {
if (scopeCallback != null) {
scopeCallback.exitScope(this);
}
if (scopeRoots.isEmpty()) {
scopes.pop();
} else {
scopeRoots.pop();
}
cfgs.pop();
}
/** Gets the current scope. */
public Scope getScope() {
Scope scope = scopes.isEmpty() ? null : scopes.peek();
if (scopeRoots.isEmpty()) {
return scope;
}
Iterator<Node> it = scopeRoots.descendingIterator
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* John Lenz
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino;
import com.google.common.base.Preconditions;
import java.util.List;
/**
* An AST construction helper class
* @author johnlenz@google.com (John Lenz)
*/
public class IR {
private IR() {}
public static Node empty() {
return new Node(Token.EMPTY);
}
public static Node function(Node name, Node params, Node body) {
Preconditions.checkState(name.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>isName());
Preconditions.checkState(params.isParamList());
Preconditions.checkState(body.isBlock());
return new Node(Token.FUNCTION, name, params, body);
}
public static Node paramList() {
return new Node(Token.PARAM_LIST);
}
public static Node paramList(Node param) {
Preconditions.checkState(param.isName());
return new Node(Token.PARAM_LIST, param);
}
public static Node paramList(Node ... params) {
Node paramList = paramList();
for (Node param : params) {
Preconditions.checkState(param.isName());
paramList.addChildToBack(param);
}
return paramList;
}
public static Node paramList(List<Node> params) {
Node paramList = paramList();
for (Node param : params) {
Preconditions.checkState(param.isName());
paramList.addChildToBack(param);
}
return paramList;
}
public static Node block() {
Node block = new Node(Token.BLOCK);
return block;
}
public static Node block(Node stmt) {
Preconditions.checkState(mayBeStatement(stmt));
Node block = new Node(Token.BLOCK, stmt);
return block;
}
public static Node block(Node ... stmts) {
Node block = block();
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatement(stmt));
block.addChildToBack(stmt);
}
return block;
}
public static Node block(List<Node> stmts) {
Node paramList = block();
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatement(stmt));
paramList.addChildToBack(stmt);
}
return paramList;
}
private static Node blockUnchecked(Node stmt) {
return new Node(Token.BLOCK, stmt);
}
public static Node script() {
// TODO(johnlenz): finish setting up the SCRIPT node
Node block = new Node(Token.SCRIPT);
return block;
}
public static Node script(Node ... stmts) {
Node block = script();
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatementNoReturn(stmt
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>));
block.addChildToBack(stmt);
}
return block;
}
public static Node script(List<Node> stmts) {
Node paramList = script();
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatementNoReturn(stmt));
paramList.addChildToBack(stmt);
}
return paramList;
}
public static Node var(Node name, Node value) {
Preconditions.checkState(name.isName() && !name.hasChildren());
Preconditions.checkState(mayBeExpression(value));
name.addChildToFront(value);
return var(name);
}
public static Node var(Node name) {
Preconditions.checkState(name.isName());
return new Node(Token.VAR, name);
}
public static Node returnNode() {
return new Node(Token.RETURN);
}
public static Node returnNode(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.RETURN, expr);
}
public static Node throwNode(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.THROW, expr);
}
public static Node exprResult(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.EXPR_RESULT, expr);
}
public static Node ifNode(Node cond, Node then) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(then.isBlock());
return new Node(Token.IF, cond, then);
}
public static Node ifNode(Node cond, Node then, Node elseNode) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(then.isBlock());
Preconditions.checkState(elseNode.isBlock());
return new Node(Token.IF, cond, then, elseNode);
}
public static Node doNode(Node body, Node cond) {
Preconditions.checkState(body.isBlock());
Preconditions.checkState(mayBeExpression(cond));
return new Node(Token.DO, body, cond);
}
public static Node forIn(Node target, Node cond, Node body) {
Preconditions.checkState(target.isVar() ||
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> mayBeExpression(target));
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(body.isBlock());
return new Node(Token.FOR, target, cond, body);
}
public static Node forNode(Node init, Node cond, Node incr, Node body) {
Preconditions.checkState(init.isVar() || mayBeExpressionOrEmpty(init));
Preconditions.checkState(mayBeExpressionOrEmpty(cond));
Preconditions.checkState(mayBeExpressionOrEmpty(incr));
Preconditions.checkState(body.isBlock());
return new Node(Token.FOR, init, cond, incr, body);
}
public static Node switchNode(Node cond, Node ... cases) {
Preconditions.checkState(mayBeExpression(cond));
Node switchNode = new Node(Token.SWITCH, cond);
for (Node caseNode : cases) {
Preconditions.checkState(caseNode.isCase() || caseNode.isDefaultCase());
switchNode.addChildToBack(caseNode);
}
return switchNode;
}
public static Node caseNode(Node expr, Node body) {
Preconditions.checkState(mayBeExpression(expr));
Preconditions.checkState(body.isBlock());
body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
return new Node(Token.CASE, expr, body);
}
public static Node defaultCase(Node body) {
Preconditions.checkState(body.isBlock());
body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
return new Node(Token.DEFAULT_CASE, body);
}
public static Node label(Node name, Node stmt) {
// TODO(johnlenz): additional validation here.
Preconditions.checkState(name.isLabelName());
Preconditions.checkState(mayBeStatement(stmt));
Node block = new Node(Token.LABEL, name, stmt);
return block;
}
public static Node labelName(String name) {
Preconditions.checkState(!name.isEmpty());
return Node.newString(Token.LABEL_NAME, name);
}
public static Node tryFinally(Node tryBody, Node finallyBody) {
Preconditions.checkState(tryBody.isBlock());
Preconditions.checkState(finallyBody.isBlock());
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
Node catchBody = block().copyInformationFrom(tryBody);
return new Node(Token.TRY, tryBody, catchBody, finallyBody);
}
public static Node tryCatch(Node tryBody, Node catchNode) {
Preconditions.checkState(tryBody.isBlock());
Preconditions.checkState(catchNode.isCatch());
Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode);
return new Node(Token.TRY, tryBody, catchBody);
}
public static Node tryCatchFinally(
Node tryBody, Node catchNode, Node finallyBody) {
Preconditions.checkState(finallyBody.isBlock());
Node tryNode = tryCatch(tryBody, catchNode);
tryNode.addChildToBack(finallyBody);
return tryNode;
}
public static Node catchNode(Node expr, Node body) {
Preconditions.checkState(expr.isName());
Preconditions.checkState(body.isBlock());
return new Node(Token.CATCH, expr, body);
}
public static Node breakNode() {
return new Node(Token.BREAK);
}
public static Node breakNode(Node name) {
// TODO(johnlenz): additional validation here.
Preconditions.checkState(name.isLabelName());
return new Node(Token.BREAK, name);
}
public static Node continueNode() {
return new Node(Token.CONTINUE);
}
public static Node continueNode(Node name) {
// TODO(johnlenz): additional validation here.
Preconditions.checkState(name.isLabelName());
return new Node(Token.CONTINUE, name);
}
//
public static Node call(Node target, Node ... args) {
Node call = new Node(Token.CALL, target);
for (Node arg : args) {
Preconditions.checkState(mayBeExpression(arg));
call.addChildToBack(arg);
}
return call;
}
public static Node newNode(Node target, Node ... args) {
Node newcall = new Node(Token.NEW, target);
for (Node arg : args) {
Preconditions.checkState(mayBeExpression(arg));
newcall.addChildToBack(arg);
}
return newcall;
}
public static Node name(String name) {
return Node.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>newString(Token.NAME, name);
}
public static Node getprop(Node target, Node prop) {
Preconditions.checkState(mayBeExpression(target));
Preconditions.checkState(prop.isString());
return new Node(Token.GETPROP, target, prop);
}
public static Node getelem(Node target, Node elem) {
Preconditions.checkState(mayBeExpression(target));
Preconditions.checkState(mayBeExpression(elem));
return new Node(Token.GETELEM, target, elem);
}
public static Node assign(Node target, Node expr) {
Preconditions.checkState(isAssignmentTarget(target));
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.ASSIGN, target, expr);
}
public static Node hook(Node cond, Node trueval, Node falseval) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(mayBeExpression(trueval));
Preconditions.checkState(mayBeExpression(falseval));
return new Node(Token.HOOK, cond, trueval, falseval);
}
public static Node comma(Node expr1, Node expr2) {
return binaryOp(Token.COMMA, expr1, expr2);
}
public static Node and(Node expr1, Node expr2) {
return binaryOp(Token.AND, expr1, expr2);
}
public static Node or(Node expr1, Node expr2) {
return binaryOp(Token.OR, expr1, expr2);
}
public static Node not(Node expr1) {
return unaryOp(Token.NOT, expr1);
}
/**
* "=="
*/
public static Node eq(Node expr1, Node expr2) {
return binaryOp(Token.EQ, expr1, expr2);
}
/**
* "==="
*/
public static Node sheq(Node expr1, Node expr2) {
return binaryOp(Token.SHEQ, expr1, expr2);
}
public static Node voidNode(Node expr1) {
return unaryOp(Token.VOID, expr1);
}
public static Node neg(Node expr1) {
return unaryOp(Token.NEG, expr1);
}
public static Node pos(Node expr1)
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> {
return unaryOp(Token.POS, expr1);
}
public static Node add(Node expr1, Node expr2) {
return binaryOp(Token.ADD, expr1, expr2);
}
public static Node sub(Node expr1, Node expr2) {
return binaryOp(Token.SUB, expr1, expr2);
}
// TODO(johnlenz): the rest of the ops
// literals
public static Node objectlit(Node ... propdefs) {
Node objectlit = new Node(Token.OBJECTLIT);
for (Node propdef : propdefs) {
Preconditions.checkState(
propdef.isStringKey() ||
propdef.isGetterDef() || propdef.isSetterDef());
Preconditions.checkState(propdef.hasOneChild());
objectlit.addChildToBack(propdef);
}
return objectlit;
}
// TODO(johnlenz): quoted props
public static Node propdef(Node string, Node value) {
Preconditions.checkState(string.isStringKey());
Preconditions.checkState(!string.hasChildren());
Preconditions.checkState(mayBeExpression(value));
string.addChildToFront(value);
return string;
}
public static Node arraylit(Node ... exprs) {
Node arraylit = new Node(Token.ARRAYLIT);
for (Node expr : exprs) {
Preconditions.checkState(mayBeExpressionOrEmpty(expr));
arraylit.addChildToBack(expr);
}
return arraylit;
}
public static Node regexp(Node expr) {
Preconditions.checkState(expr.isString());
return new Node(Token.REGEXP, expr);
}
public static Node regexp(Node expr, Node flags) {
Preconditions.checkState(expr.isString());
Preconditions.checkState(flags.isString());
return new Node(Token.REGEXP, expr, flags);
}
public static Node string(String s) {
return Node.newString(s);
}
public static Node stringKey(String s) {
return Node.newString(Token.STRING_KEY, s);
}
public static Node number(double d) {
return Node.newNumber(d);
}
public static Node thisNode() {
return new Node(Token.THIS);
}
public
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> static Node trueNode() {
return new Node(Token.TRUE);
}
public static Node falseNode() {
return new Node(Token.FALSE);
}
public static Node nullNode() {
return new Node(Token.NULL);
}
// helper methods
private static Node binaryOp(int token, Node expr1, Node expr2) {
Preconditions.checkState(mayBeExpression(expr1));
Preconditions.checkState(mayBeExpression(expr2));
return new Node(token, expr1, expr2);
}
private static Node unaryOp(int token, Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(token, expr);
}
private static boolean mayBeExpressionOrEmpty(Node n) {
return n.isEmpty() || mayBeExpression(n);
}
private static boolean isAssignmentTarget(Node n) {
return n.isName() || n.isGetProp() || n.isGetElem();
}
// NOTE: some nodes are neither statements nor expression nodes:
// SCRIPT, LABEL_NAME, PARAM_LIST, CASE, DEFAULT_CASE, CATCH
// GETTER_DEF, SETTER_DEF
/**
* It isn't possible to always determine if a detached node is a expression,
* so make a best guess.
*/
private static boolean mayBeStatementNoReturn(Node n) {
switch (n.getType()) {
case Token.EMPTY:
case Token.FUNCTION:
// EMPTY and FUNCTION are used both in expression and statement
// contexts
return true;
case Token.BLOCK:
case Token.BREAK:
case Token.CONST:
case Token.CONTINUE:
case Token.DEBUGGER:
case Token.DO:
case Token.EXPR_RESULT:
case Token.FOR:
case Token.IF:
case Token.LABEL:
case Token.SWITCH:
case Token.THROW:
case Token.TRY:
case Token.VAR:
case Token.WHILE:
case Token.WITH:
return true;
default:
return false;
}
}
/**
* It isn't possible to always determine if a detached node is a expression,
* so make a best guess.
*/
private static boolean mayBeStatement(Node n) {
if (!may
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
* Creates a map of String->Node from a map of String->Number/String/Boolean.
*/
private static Map<String, Node> getReplacementsHelper(
Map<String, Object> source) {
Map<String, Node> map = Maps.newHashMap();
for (Map.Entry<String, Object> entry : source.entrySet()) {
String name = entry.getKey();
Object value = entry.getValue();
if (value instanceof Boolean) {
map.put(name, NodeUtil.booleanNode(((Boolean) value).booleanValue()));
} else if (value instanceof Integer) {
map.put(name, IR.number(((Integer) value).intValue()));
} else if (value instanceof Double) {
map.put(name, IR.number(((Double) value).doubleValue()));
} else {
Preconditions.checkState(value instanceof String);
map.put(name, IR.string((String) value));
}
}
return map;
}
/**
* Sets the value of the {@code @define} variable in JS
* to a boolean literal.
*/
public void setDefineToBooleanLiteral(String defineName, boolean value) {
defineReplacements.put(defineName, new Boolean(value));
}
/**
* Sets the value of the {@code @define} variable in JS to a
* String literal.
*/
public void setDefineToStringLiteral(String defineName, String value) {
defineReplacements.put(defineName, value);
}
/**
* Sets the value of the {@code @define} variable in JS to a
* number literal.
*/
public void setDefineToNumberLiteral(String defineName, int value) {
defineReplacements.put(defineName, new Integer(value));
}
/**
* Sets the value of the {@code @define} variable in JS to a
* number literal.
*/
public void setDefineToDoubleLiteral(String defineName, double value) {
defineReplacements.put(defineName, new Double(value));
}
/**
* Sets the value of the tweak in JS
* to a boolean literal.
*/
public void setTweakToBooleanLiteral(String tweakId, boolean value) {
tweakReplacements.put(tweakId, new Boolean(value));
}
/**
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>boolean value) {
this.chainCalls = value;
}
/**
* If true, accept `const' keyword.
*/
public void setAcceptConstKeyword(boolean value) {
this.acceptConstKeyword = value;
}
/**
* Enable run-time type checking, which adds JS type assertions for debugging.
*
* @param logFunction A JS function to be used for logging run-time type
* assertion failures.
*/
public void enableRuntimeTypeCheck(String logFunction) {
this.runtimeTypeCheck = true;
this.runtimeTypeCheckLogFunction = logFunction;
}
public void disableRuntimeTypeCheck() {
this.runtimeTypeCheck = false;
}
public void setGenerateExports(boolean generateExports) {
this.generateExports = generateExports;
}
public void setCodingConvention(CodingConvention codingConvention) {
this.codingConvention = codingConvention;
}
public CodingConvention getCodingConvention() {
return codingConvention;
}
/**
* Sets dependency options. See the DependencyOptions class for more info.
* This supersedes manageClosureDependencies.
*/
public void setDependencyOptions(DependencyOptions options) {
Preconditions.checkNotNull(options);
this.dependencyOptions = options;
}
/**
* Sort inputs by their goog.provide/goog.require calls, and prune inputs
* whose symbols are not required.
*/
public void setManageClosureDependencies(boolean newVal) {
dependencyOptions.setDependencySorting(
newVal || dependencyOptions.shouldSortDependencies());
dependencyOptions.setDependencyPruning(
newVal || dependencyOptions.shouldPruneDependencies());
dependencyOptions.setMoocherDropping(false);
manageClosureDependencies = newVal;
}
/**
* Sort inputs by their goog.provide/goog.require calls.
*
* @param entryPoints Entry points to the program. Must be goog.provide'd
* symbols. Any goog.provide'd symbols that are not a transitive
* dependency of the entry points will be deleted.
* Files without goog.provides, and their dependencies,
* will always be left in.
*/
public void setManageClosureDependencies(List<String> entryPoints) {
Preconditions.checkNotNull(entryPoints);
setManageClosureDependencies(true);
dependencyOptions.setEntryPoints
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> root);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Note: Constant properties annotations are not propagated.
if (n.isName()) {
if (n.getString().isEmpty()) {
return;
}
JSDocInfo info = null;
// Find the JSDocInfo for a top-level variable.
Var var = t.getScope().getVar(n.getString());
if (var != null) {
info = var.getJSDocInfo();
}
boolean shouldBeConstant =
(info != null && info.isConstant()) ||
NodeUtil.isConstantByConvention(
compiler.getCodingConvention(), n, parent);
boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME);
if (shouldBeConstant && !isMarkedConstant) {
if (assertOnChange) {
String name = n.getString();
throw new IllegalStateException(
"Unexpected const change.\n" +
" name: "+ name + "\n" +
" parent:" + n.getParent().toStringTree());
}
n.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
}
}
/**
* Walk the AST tree and verify that constant names are used consistently.
*/
static class VerifyConstants extends AbstractPostOrderCallback
implements CompilerPass {
final private AbstractCompiler compiler;
final private boolean checkUserDeclarations;
VerifyConstants(AbstractCompiler compiler, boolean checkUserDeclarations) {
this.compiler = compiler;
this.checkUserDeclarations = checkUserDeclarations;
}
@Override
public void process(Node externs, Node root) {
Node externsAndJs = root.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(externsAndJs.hasChild(externs));
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
}
private Map<String, Boolean> constantMap = Maps.newHashMap();
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
String name = n.getString();
if (n.getString().isEmpty()) {
return;
}
boolean isConst = n.getBooleanProp(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node.IS_CONSTANT_NAME);
if (checkUserDeclarations) {
boolean expectedConst = false;
CodingConvention convention = compiler.getCodingConvention();
if (NodeUtil.isConstantName(n)
|| NodeUtil.isConstantByConvention(convention, n, parent)) {
expectedConst = true;
} else {
expectedConst = false;
JSDocInfo info = null;
Var var = t.getScope().getVar(n.getString());
if (var != null) {
info = var.getJSDocInfo();
}
if (info != null && info.isConstant()) {
expectedConst = true;
} else {
expectedConst = false;
}
}
if (expectedConst) {
Preconditions.checkState(expectedConst == isConst,
"The name %s is not annotated as constant.", name);
} else {
Preconditions.checkState(expectedConst == isConst,
"The name %s should not be annotated as constant.", name);
}
}
Boolean value = constantMap.get(name);
if (value == null) {
constantMap.put(name, isConst);
} else {
Preconditions.checkState(value.booleanValue() == isConst,
"The name %s is not consistently annotated as constant.", name);
}
}
}
}
/**
* Simplify the AST:
* - VAR declarations split, so they represent exactly one child
* declaration.
* - WHILEs are converted to FORs
* - FOR loop are initializers are moved out of the FOR structure
* - LABEL node of children other than LABEL, BLOCK, WHILE, FOR, or DO are
* moved into a block.
* - Add constant annotations based on coding convention.
*/
static class NormalizeStatements implements Callback {
private final AbstractCompiler compiler;
private final boolean assertOnChange;
NormalizeStatements(AbstractCompiler compiler, boolean assertOnChange) {
this.compiler = compiler;
this.assertOnChange = assertOnChange;
}
private void reportCodeChange(String changeDescription) {
if (assertOnChange) {
throw new IllegalStateException(
"Normalize constraints violated:\n" + changeDescription);
}
compiler.reportCodeChange();
}
@Override
public boolean shouldTraverse(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>NodeTraversal t, Node n, Node parent) {
doStatementNormalizations(t, n, parent);
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.WHILE:
if (CONVERT_WHILE_TO_FOR) {
Node expr = n.getFirstChild();
n.setType(Token.FOR);
Node empty = IR.empty();
empty.copyInformationFrom(n);
n.addChildBefore(empty, expr);
n.addChildAfter(empty.cloneNode(), expr);
reportCodeChange("WHILE node");
}
break;
case Token.FUNCTION:
normalizeFunctionDeclaration(n);
break;
case Token.NAME:
case Token.STRING:
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
if (!compiler.getLifeCycleStage().isNormalizedObfuscated()) {
annotateConstantsByConvention(n, parent);
}
break;
}
}
/**
* Mark names and properties that are constants by convention.
*/
private void annotateConstantsByConvention(Node n, Node parent) {
Preconditions.checkState(
n.isName()
|| n.isString()
|| n.isStringKey()
|| n.isGetterDef()
|| n.isSetterDef());
// There are only two cases where a string token
// may be a variable reference: The right side of a GETPROP
// or an OBJECTLIT key.
boolean isObjLitKey = NodeUtil.isObjectLitKey(n, parent);
boolean isProperty = isObjLitKey ||
(parent.isGetProp() &&
parent.getLastChild() == n);
if (n.isName() || isProperty) {
boolean isMarkedConstant = n.getBooleanProp(Node.IS_CONSTANT_NAME);
if (!isMarkedConstant &&
NodeUtil.isConstantByConvention(
compiler.getCodingConvention(), n, parent)) {
if (assertOnChange) {
String name = n.getString();
throw new IllegalStateException(
"Unexpected const change.\n" +
" name: "+ name + "\n" +
" parent:" + n.getParent().toStringTree());
}
n.put
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>BooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
}
/**
* Rewrite named unhoisted functions declarations to a known
* consistent behavior so we don't to different logic paths for the same
* code. From:
* function f() {}
* to:
* var f = function () {};
*/
private void normalizeFunctionDeclaration(Node n) {
Preconditions.checkState(n.isFunction());
if (!NodeUtil.isFunctionExpression(n)
&& !NodeUtil.isHoistedFunctionDeclaration(n)) {
rewriteFunctionDeclaration(n);
}
}
/**
* Rewrite the function declaration from:
* function x() {}
* FUNCTION
* NAME
* LP
* BLOCK
* to:
* var x = function() {};
* VAR
* NAME
* FUNCTION
* NAME (w/ empty string)
* LP
* BLOCK
*/
private void rewriteFunctionDeclaration(Node n) {
// Prepare a spot for the function.
Node oldNameNode = n.getFirstChild();
Node fnNameNode = oldNameNode.cloneNode();
Node var = IR.var(fnNameNode).srcref(n);
// Prepare the function
oldNameNode.setString("");
// Move the function
Node parent = n.getParent();
parent.replaceChild(n, var);
fnNameNode.addChildToFront(n);
reportCodeChange("Function declaration");
}
/**
* Do normalizations that introduce new siblings or parents.
*/
private void doStatementNormalizations(
NodeTraversal t, Node n, Node parent) {
if (n.isLabel()) {
normalizeLabels(n);
}
// Only inspect the children of SCRIPTs, BLOCKs and LABELs, as all these
// are the only legal place for VARs and FOR statements.
if (NodeUtil.isStatementBlock(n) || n.isLabel()) {
extractForInitializer(n, null, null);
}
// Only inspect the children of SCRIPTs, BLOCKs, as all these
// are the only legal place for VARs.
if (NodeUtil.isStatementBlock(n)) {
splitVarDeclarations(n);
}
if (n.isFunction()) {
move
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>NamedFunctions(n.getLastChild());
}
}
// TODO(johnlenz): Move this to NodeTypeNormalizer once the unit tests are
// fixed.
/**
* Limit the number of special cases where LABELs need to be handled. Only
* BLOCK and loops are allowed to be labeled. Loop labels must remain in
* place as the named continues are not allowed for labeled blocks.
*/
private void normalizeLabels(Node n) {
Preconditions.checkArgument(n.isLabel());
Node last = n.getLastChild();
switch (last.getType()) {
case Token.LABEL:
case Token.BLOCK:
case Token.FOR:
case Token.WHILE:
case Token.DO:
return;
default:
Node block = IR.block();
block.copyInformationFrom(last);
n.replaceChild(last, block);
block.addChildToFront(last);
reportCodeChange("LABEL normalization");
return;
}
}
/**
* Bring the initializers out of FOR loops. These need to be placed
* before any associated LABEL nodes. This needs to be done from the top
* level label first so this is called as a pre-order callback (from
* shouldTraverse).
*
* @param n The node to inspect.
* @param before The node to insert the initializer before.
* @param beforeParent The parent of the node before which the initializer
* will be inserted.
*/
private void extractForInitializer(
Node n, Node before, Node beforeParent) {
for (Node next, c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
Node insertBefore = (before == null) ? c : before;
Node insertBeforeParent = (before == null) ? n : beforeParent;
switch (c.getType()) {
case Token.LABEL:
extractForInitializer(c, insertBefore, insertBeforeParent);
break;
case Token.FOR:
if (NodeUtil.isForIn(c)) {
Node first = c.getFirstChild();
if (first.isVar()) {
// Transform:
// for (var a = 1 in b) {}
// to:
// var a = 1; for (a in b) {};
Node
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> newStatement = first;
// Clone just the node, to remove any initialization.
Node name = newStatement.getFirstChild().cloneNode();
first.getParent().replaceChild(first, name);
insertBeforeParent.addChildBefore(newStatement, insertBefore);
reportCodeChange("FOR-IN var declaration");
}
} else if (!c.getFirstChild().isEmpty()) {
Node init = c.getFirstChild();
Node empty = IR.empty();
empty.copyInformationFrom(c);
c.replaceChild(init, empty);
Node newStatement;
// Only VAR statements, and expressions are allowed,
// but are handled differently.
if (init.isVar()) {
newStatement = init;
} else {
newStatement = NodeUtil.newExpr(init);
}
insertBeforeParent.addChildBefore(newStatement, insertBefore);
reportCodeChange("FOR initializer");
}
break;
}
}
}
/**
* Split a var node such as:
* var a, b;
* into individual statements:
* var a;
* var b;
* @param n The whose children we should inspect.
*/
private void splitVarDeclarations(Node n) {
for (Node next, c = n.getFirstChild(); c != null; c = next) {
next = c.getNext();
if (c.isVar()) {
if (assertOnChange && !c.hasChildren()) {
throw new IllegalStateException("Empty VAR node.");
}
while (c.getFirstChild() != c.getLastChild()) {
Node name = c.getFirstChild();
c.removeChild(name);
Node newVar = IR.var(name).srcref(n);
n.addChildBefore(newVar, c);
reportCodeChange("VAR with multiple children");
}
}
}
}
/**
* Move all the functions that are valid at the execution of the first
* statement of the function to the beginning of the function definition.
*/
private void moveNamedFunctions(Node functionBody) {
Preconditions.checkState(
functionBody.getParent().isFunction());
Node previous = null;
Node current = functionBody.getFirstChild();
// Skip any declarations at the beginning of the function body, they
// are already in the right place.
while (current
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> != null && NodeUtil.isFunctionDeclaration(current)) {
previous = current;
current = current.getNext();
}
// Find any remaining declarations and move them.
Node insertAfter = previous;
while (current != null) {
// Save off the next node as the current node maybe removed.
Node next = current.getNext();
if (NodeUtil.isFunctionDeclaration(current)) {
// Remove the declaration from the body.
Preconditions.checkNotNull(previous);
functionBody.removeChildAfter(previous);
// Read the function at the top of the function body (after any
// previous declarations).
insertAfter = addToFront(functionBody, current, insertAfter);
reportCodeChange("Move function declaration not at top of function");
} else {
// Update the previous only if the current node hasn't been moved.
previous = current;
}
current = next;
}
}
/**
* @param after The child node to insert the newChild after, or null if
* newChild should be added to the front of parent's child list.
* @return The inserted child node.
*/
private Node addToFront(Node parent, Node newChild, Node after) {
if (after == null) {
parent.addChildToFront(newChild);
} else {
parent.addChildAfter(newChild, after);
}
return newChild;
}
}
/**
* Remove duplicate VAR declarations.
*/
private void removeDuplicateDeclarations(Node externs, Node root) {
Callback tickler = new ScopeTicklingCallback();
ScopeCreator scopeCreator = new SyntacticScopeCreator(
compiler, new DuplicateDeclarationHandler());
NodeTraversal t = new NodeTraversal(compiler, tickler, scopeCreator);
t.traverseRoots(externs, root);
}
/**
* ScopeCreator duplicate declaration handler.
*/
private final class DuplicateDeclarationHandler implements
SyntacticScopeCreator.RedeclarationHandler {
private Set<Var> hasOkDuplicateDeclaration = Sets.newHashSet();
/**
* Remove duplicate VAR declarations encountered discovered during
* scope creation.
*/
@Override
public void onRedeclaration(
Scope s, String name, Node n, CompilerInput input) {
Preconditions.checkState(n.isName());
Node parent = n.getParent();
Var v = s.getVar(name);
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
if (v != null && s.isGlobal()) {
// We allow variables to be duplicate declared if one
// declaration appears in source and the other in externs.
// This deals with issues where a browser built-in is declared
// in one browser but not in another.
if (v.isExtern() && !input.isExtern()) {
if (hasOkDuplicateDeclaration.add(v)) {
return;
}
}
}
// If name is "arguments", Var maybe null.
if (v != null && v.getParentNode().isCatch()) {
// Redeclaration of a catch expression variable is hard to model
// without support for "with" expressions.
// The ECMAScript spec (section 12.14), declares that a catch
// "catch (e) {}" is handled like "with ({'e': e}) {}" so that
// "var e" would refer to the scope variable, but any following
// reference would still refer to "e" of the catch expression.
// Until we have support for this disallow it.
// Currently the Scope object adds the catch expression to the
// function scope, which is technically not true but a good
// approximation for most uses.
// TODO(johnlenz): Consider improving how scope handles catch
// expression.
// Use the name of the var before it was made unique.
name = MakeDeclaredNamesUnique.ContextualRenameInverter.getOrginalName(
name);
compiler.report(
JSError.make(
input.getName(), n,
CATCH_BLOCK_VAR_ERROR, name));
} else if (v != null && parent.isFunction()) {
if (v.getParentNode().isVar()) {
s.undeclare(v);
s.declare(name, n, n.getJSType(), v.input);
replaceVarWithAssignment(v.getNameNode(), v.getParentNode(),
v.getParentNode().getParent());
}
} else if (parent.isVar()) {
Preconditions.checkState(parent.hasOneChild());
replaceVarWithAssignment(n, parent, parent.getParent());
}
}
/**
* Remove the parent VAR. There are three cases that need to be handled:
* 1) "var a = b;" which is replaced
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> with "a = b"
* 2) "label:var a;" which is replaced with "label:;". Ideally, the
* label itself would be removed but that is not possible in the
* context in which "onRedeclaration" is called.
* 3) "for (var a in b) ..." which is replaced with "for (a in b)..."
* Cases we don't need to handle are VARs with multiple children,
* which have already been split into separate declarations, so there
* is no need to handle that here, and "for (var a;;);", which has
* been moved out of the loop.
* The result of this is that in each case the parent node is replaced
* which is generally dangerous in a traversal but is fine here with
* the scope creator, as the next node of interest is the parent's
* next sibling.
*/
private void replaceVarWithAssignment(Node n, Node parent, Node gramps) {
if (n.hasChildren()) {
// The * is being initialize, preserve the new value.
parent.removeChild(n);
// Convert "var name = value" to "name = value"
Node value = n.getFirstChild();
n.removeChild(value);
Node replacement = IR.assign(n, value);
replacement.copyInformationFrom(parent);
gramps.replaceChild(parent, NodeUtil.newExpr(replacement));
} else {
// It is an empty reference remove it.
if (NodeUtil.isStatementBlock(gramps)) {
gramps.removeChild(parent);
} else if (gramps.isFor()) {
// This is the "for (var a in b)..." case. We don't need to worry
// about initializers in "for (var a;;)..." as those are moved out
// as part of the other normalizations.
parent.removeChild(n);
gramps.replaceChild(parent, n);
} else {
Preconditions.checkState(gramps.isLabel());
// We should never get here. LABELs with a single VAR statement should
// already have been normalized to have a BLOCK.
throw new IllegalStateException("Unexpected LABEL");
}
}
reportCodeChange("Duplicate
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * @param externsRoot The root of the externs parse tree.
* @param jsRoot The root of the input parse tree to be checked.
*/
@Override
public void process(Node externsRoot, Node jsRoot) {
Node externsAndJs = jsRoot.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(
externsRoot == null || externsAndJs.hasChild(externsRoot));
inferTypes(externsAndJs);
}
/** Entry point for type inference when running over part of the tree. */
void inferTypes(Node node) {
NodeTraversal inferTypes = new NodeTraversal(
compiler, new TypeInferringCallback(), scopeCreator);
inferTypes.traverseWithScope(node, topScope);
}
void inferTypes(NodeTraversal t, Node n, Scope scope) {
TypeInference typeInference =
new TypeInference(
compiler, computeCfg(n), reverseInterpreter, scope,
assertionFunctionsMap);
try {
typeInference.analyze();
// Resolve any new type names found during the inference.
compiler.getTypeRegistry().resolveTypesInScope(scope);
} catch (DataFlowAnalysis.MaxIterationsExceededException e) {
compiler.report(t.makeError(n, DATAFLOW_ERROR));
}
}
private class TypeInferringCallback extends AbstractScopedCallback {
@Override
public void enterScope(NodeTraversal t) {
inferTypes(t, t.getCurrentNode(), t.getScope());
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// Do nothing
}
}
private ControlFlowGraph<Node> computeCfg(Node n) {
ControlFlowAnalysis cfa = new ControlFlowAnalysis(compiler, false, false);
cfa.process(null, n);
return cfa.getCfg();
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> + i, expected[i]));
}
compiler.init(externsInputs, inputs, getOptions());
Node root = compiler.parseInputs();
assertTrue("Unexpected parse error(s): " +
Joiner.on("\n").join(compiler.getErrors()), root != null);
Node externsRoot = root.getFirstChild();
Node mainRoot = externsRoot.getNext();
// Only run the normalize pass, if asked.
if (normalizeEnabled && normalizeExpected && !compiler.hasErrors()) {
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot);
}
return mainRoot;
}
protected Node parseExpectedJs(String expected) {
return parseExpectedJs(new String[] {expected});
}
/**
* Generates a list of modules from a list of inputs, such that each module
* depends on the module before it.
*/
static JSModule[] createModuleChain(String... inputs) {
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[i - 1]);
}
return modules;
}
/**
* Generates a list of modules from a list of inputs, such that each module
* depends on the first module.
*/
static JSModule[] createModuleStar(String... inputs) {
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[0]);
}
return modules;
}
/**
* Generates a list of modules from a list of inputs, such that modules
* form a bush formation. In a bush formation, module 2 depends
* on module 1, and all other modules depend on module 2.
*/
static JSModule[] createModuleBush(String ... inputs) {
Preconditions.checkState(inputs.length > 2);
JSModule[] modules = createModules(inputs);
for (int i = 1; i < modules.length; i++) {
modules[i].addDependency(modules[i == 1 ? 0 : 1]);
}
return modules;
}
/**
* Generates a list of modules from a list
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> So once a child scope is created, this flow scope may not be modified.
private boolean frozen = false;
// The last slot defined in this flow instruction, and the head of the
// linked list of slots.
private LinkedFlowSlot lastSlot;
private LinkedFlowScope(FlatFlowScopeCache cache,
LinkedFlowScope directParent) {
this.cache = cache;
if (directParent == null) {
this.lastSlot = null;
this.depth = 0;
this.parent = cache.linkedEquivalent;
} else {
this.lastSlot = directParent.lastSlot;
this.depth = directParent.depth + 1;
this.parent = directParent;
}
}
LinkedFlowScope(FlatFlowScopeCache cache) {
this(cache, null);
}
LinkedFlowScope(LinkedFlowScope directParent) {
this(directParent.cache, directParent);
}
/** Gets the function scope for this flow scope. */
private Scope getFunctionScope() {
return cache.functionScope;
}
/** Whether this flows from a bottom scope. */
private boolean flowsFromBottom() {
return getFunctionScope().isBottom();
}
/**
* Creates an entry lattice for the flow.
*/
public static LinkedFlowScope createEntryLattice(Scope scope) {
return new LinkedFlowScope(new FlatFlowScopeCache(scope));
}
@Override
public void inferSlotType(String symbol, JSType type) {
Preconditions.checkState(!frozen);
lastSlot = new LinkedFlowSlot(symbol, type, lastSlot);
depth++;
cache.dirtySymbols.add(symbol);
}
@Override
public void inferQualifiedSlot(Node node, String symbol, JSType bottomType,
JSType inferredType) {
Scope functionScope = getFunctionScope();
if (functionScope.isLocal()) {
if (functionScope.getVar(symbol) == null && !functionScope.isBottom()) {
functionScope.declare(symbol, node, bottomType, null);
}
inferSlotType(symbol, inferredType);
}
}
@Override
public JSType getTypeOfThis() {
return cache.functionScope.getTypeOfThis();
}
@Override
public Node getRootNode() {
return getFunctionScope().getRootNode();
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> both 0 based.
int lineBaseOffset = 1;
if (generator instanceof SourceMapGeneratorV1
|| generator instanceof SourceMapGeneratorV2) {
lineBaseOffset = 0;
}
generator.addMapping(
sourceFile, originalName,
new FilePosition(node.getLineno() - lineBaseOffset, node.getCharno()),
outputStartPosition, outputEndPosition);
}
/**
* @param sourceFile The source file location to fixup.
* @return a remapped source file.
*/
private String fixupSourceLocation(String sourceFile) {
if (prefixMappings.isEmpty()) {
return sourceFile;
}
String fixed = sourceLocationFixupCache.get(sourceFile);
if (fixed != null) {
return fixed;
}
// Replace the first prefix found with its replacement
for (LocationMapping mapping : prefixMappings) {
if (sourceFile.startsWith(mapping.prefix)) {
fixed = mapping.replacement + sourceFile.substring(
mapping.prefix.length());
break;
}
}
// If none of the mappings match then use the original file path.
if (fixed == null) {
fixed = sourceFile;
}
sourceLocationFixupCache.put(sourceFile, fixed);
return fixed;
}
public void appendTo(Appendable out, String name) throws IOException {
generator.appendTo(out, name);
}
public void reset() {
generator.reset();
sourceLocationFixupCache.clear();
}
public void setStartingPosition(int offsetLine, int offsetIndex) {
generator.setStartingPosition(offsetLine, offsetIndex);
}
public void setWrapperPrefix(String prefix) {
generator.setWrapperPrefix(prefix);
}
public void validate(boolean validate) {
generator.validate(validate);
}
/**
* @param sourceMapLocationMappings
*/
public void setPrefixMappings(List<LocationMapping> sourceMapLocationMappings) {
this.prefixMappings = sourceMapLocationMappings;
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> reference is resolved.
*
* The {@code UnresolvedType} will behave like an opaque unknown type.
* When its {@code #resolve} method is called, it will return the underlying
* type. The underlying type can resolve to any JS type.<p>
*
* @author nicksantos@google.com (Nick Santos)
*/
class UnresolvedTypeExpression extends UnknownType {
private static final long serialVersionUID = 1L;
private final Node typeExpr;
private final String sourceName;
/**
* Create a named type based on the reference.
*/
UnresolvedTypeExpression(JSTypeRegistry registry, Node typeExpr,
String sourceName) {
super(registry, false);
Preconditions.checkNotNull(typeExpr);
this.typeExpr = typeExpr;
this.sourceName = sourceName;
}
/**
* Resolve the referenced type within the enclosing scope.
*/
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> enclosing) {
return registry.createFromTypeNodes(typeExpr, sourceName, enclosing);
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> modules;
/**
* Lists of modules at each depth. <code>modulesByDepth.get(3)</code> is a
* list of the modules at depth 3, for example.
*/
private List<List<JSModule>> modulesByDepth;
/**
* dependencyMap is a cache of dependencies that makes the dependsOn
* function faster. Each map entry associates a starting
* JSModule with the set of JSModules that are transitively dependent on the
* starting module.
*
* If the cache returns null, then the entry hasn't been filled in for that
* module.
*
* dependencyMap should be filled from leaf to root so that
* getTransitiveDepsDeepestFirst can use its results directly.
*/
private Map<JSModule, Set<JSModule>> dependencyMap = Maps.newHashMap();
/**
* Creates a module graph from a list of modules in dependency order.
*/
public JSModuleGraph(JSModule[] modulesInDepOrder) {
this(ImmutableList.copyOf(modulesInDepOrder));
}
/**
* Creates a module graph from a list of modules in dependency order.
*/
public JSModuleGraph(List<JSModule> modulesInDepOrder) {
Preconditions.checkState(
modulesInDepOrder.size() == Sets.newHashSet(modulesInDepOrder).size(),
"Found duplicate modules");
modules = ImmutableList.copyOf(modulesInDepOrder);
modulesByDepth = Lists.newArrayList();
for (JSModule module : modulesInDepOrder) {
int depth = 0;
for (JSModule dep : module.getDependencies()) {
int depDepth = dep.getDepth();
if (depDepth < 0) {
throw new ModuleDependenceException(String.format(
"Modules not in dependency order: %s preceded %s",
module.getName(), dep.getName()),
module, dep);
}
depth = Math.max(depth, depDepth + 1);
}
module.setDepth(depth);
if (depth == modulesByDepth.size()) {
modulesByDepth.add(new ArrayList<JSModule>());
}
modulesByDepth.get(depth).add(module);
}
}
/**
* Gets an iterable over all modules in dependency order.
*/
Iterable<JSModule
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>ing(true);
depOptions.setEntryPoints(entryPoints);
return manageDependencies(depOptions, inputs);
}
/**
* Apply the dependency options to the list of sources, returning a new
* source list re-ordering and dropping files as necessary.
* This module graph will be updated to reflect the new list.
*
* @param inputs The original list of sources. Used to ensure that the sort
* is stable.
* @throws CircularDependencyException if there is a circular dependency
* between the provides and requires.
* @throws MissingProvideException if an entry point was not provided
* by any of the inputs.
* @see DependencyOptions for more info on how this works.
*/
public List<CompilerInput> manageDependencies(
DependencyOptions depOptions,
List<CompilerInput> inputs)
throws CircularDependencyException, MissingProvideException {
SortedDependencies<CompilerInput> sorter =
new SortedDependencies<CompilerInput>(inputs);
Set<CompilerInput> entryPointInputs = Sets.newLinkedHashSet();
if (depOptions.shouldPruneDependencies()) {
if (!depOptions.shouldDropMoochers()) {
entryPointInputs.addAll(sorter.getInputsWithoutProvides());
}
for (String entryPoint : depOptions.getEntryPoints()) {
entryPointInputs.add(sorter.getInputProviding(entryPoint));
}
CompilerInput baseJs = sorter.maybeGetInputProviding("goog");
if (baseJs != null) {
entryPointInputs.add(baseJs);
}
} else {
entryPointInputs.addAll(inputs);
}
// The order of inputs, sorted independently of modules.
List<CompilerInput> absoluteOrder =
sorter.getDependenciesOf(inputs, depOptions.shouldSortDependencies());
// Figure out which sources *must* be in each module.
ListMultimap<JSModule, CompilerInput> entryPointInputsPerModule =
LinkedListMultimap.create();
for (CompilerInput input : entryPointInputs) {
JSModule module = input.getModule();
Preconditions.checkNotNull(module);
entryPointInputsPerModule.put(module, input);
}
// Clear the modules of their inputs. This also nulls out
// the input's reference to its module.
for (JSModule module : getAllModules()) {
module.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>MODULE_DEPENDENCY",
"module {0} cannot reference {2}, defined in " +
"module {1}");
static final DiagnosticType NAME_REFERENCE_IN_EXTERNS_ERROR =
DiagnosticType.warning(
"JSC_NAME_REFERENCE_IN_EXTERNS",
"accessing name {0} in externs has no effect");
static final DiagnosticType UNDEFINED_EXTERN_VAR_ERROR =
DiagnosticType.warning(
"JSC_UNDEFINED_EXTERN_VAR_ERROR",
"name {0} is not undefined in the externs.");
private Node synthesizedExternsRoot = null;
// Vars that still need to be declared in externs. These will be declared
// at the end of the pass, or when we see the equivalent var declared
// in the normal code.
private final Set<String> varsToDeclareInExterns = Sets.newHashSet();
private final AbstractCompiler compiler;
// Whether this is the post-processing sanity check.
private final boolean sanityCheck;
// Whether extern checks emit error.
private final boolean strictExternCheck;
VarCheck(AbstractCompiler compiler) {
this(compiler, false);
}
VarCheck(AbstractCompiler compiler, boolean sanityCheck) {
this.compiler = compiler;
this.strictExternCheck = compiler.getErrorLevel(
JSError.make("", 0, 0, UNDEFINED_EXTERN_VAR_ERROR)) == CheckLevel.ERROR;
this.sanityCheck = sanityCheck;
}
@Override
public void process(Node externs, Node root) {
// Don't run externs-checking in sanity check mode. Normalization will
// remove duplicate VAR declarations, which will make
// externs look like they have assigns.
if (!sanityCheck) {
NodeTraversal.traverse(compiler, externs, new NameRefInExternsCheck());
}
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
for (String varName : varsToDeclareInExterns) {
createSynthesizedExternVar(varName);
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
Preconditions.checkState(scriptRoot.isScript());
NodeTraversal t = new NodeTraversal(compiler, this
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>);
// Note we use the global scope to prevent wrong "undefined-var errors" on
// variables that are defined in other JS files.
t.traverseWithScope(scriptRoot,
SyntacticScopeCreator.generateUntypedTopScope(compiler));
// TODO(bashir) Check if we need to createSynthesizedExternVar like process.
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (!n.isName()) {
return;
}
String varName = n.getString();
// Only a function can have an empty name.
if (varName.isEmpty()) {
Preconditions.checkState(parent.isFunction());
Preconditions.checkState(NodeUtil.isFunctionExpression(parent));
return;
}
// Check if this is a declaration for a var that has been declared
// elsewhere. If so, mark it as a duplicate.
if ((parent.isVar() ||
NodeUtil.isFunctionDeclaration(parent)) &&
varsToDeclareInExterns.contains(varName)) {
createSynthesizedExternVar(varName);
n.addSuppression("duplicate");
}
// Check that the var has been declared.
Scope scope = t.getScope();
Scope.Var var = scope.getVar(varName);
if (var == null) {
if (NodeUtil.isFunctionExpression(parent)) {
// e.g. [ function foo() {} ], it's okay if "foo" isn't defined in the
// current scope.
} else {
// The extern checks are stricter, don't report a second error.
if (!strictExternCheck || !t.getInput().isExtern()) {
t.report(n, UNDEFINED_VAR_ERROR, varName);
}
if (sanityCheck) {
throw new IllegalStateException("Unexpected variable " + varName);
} else {
createSynthesizedExternVar(varName);
scope.getGlobalScope().declare(varName, n,
null, getSynthesizedExternsInput());
}
}
return;
}
CompilerInput currInput = t.getInput();
CompilerInput varInput = var.input;
if (currInput == varInput || currInput == null || varInput == null) {
// The variable was defined in the same file. This is fine.
return
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
this.depth = -1;
}
/** Gets the module name. */
@Override
public String getName() {
return name;
}
@Override
public List<String> getProvides() {
return ImmutableList.<String>of(name);
}
@Override
public List<String> getRequires() {
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (JSModule m : deps) {
builder.add(m.getName());
}
return builder.build();
}
@Override
public String getPathRelativeToClosureBase() {
throw new UnsupportedOperationException();
}
/** Adds a source file input to this module. */
public void add(SourceFile file) {
add(new CompilerInput(file));
}
/** Adds a source file input to this module. */
public void addFirst(SourceFile file) {
addFirst(new CompilerInput(file));
}
/** Adds a source code input to this module. */
public void add(CompilerInput input) {
inputs.add(input);
input.setModule(this);
}
/**
* Adds a source code input to this module. Call only if the input might
* already be associated with a module. Otherwise, use
* add(CompilerInput input).
*/
void addAndOverrideModule(CompilerInput input) {
inputs.add(input);
input.overrideModule(this);
}
/** Adds a source code input to this module. */
public void addFirst(CompilerInput input) {
inputs.add(0, input);
input.setModule(this);
}
/** Adds a source code input to this module directly after other. */
public void addAfter(CompilerInput input, CompilerInput other) {
Preconditions.checkState(inputs.contains(other));
inputs.add(inputs.indexOf(other), input);
input.setModule(this);
}
/** Adds a dependency on another module. */
public void addDependency(JSModule dep) {
Preconditions.checkNotNull(dep);
Preconditions.checkState(dep != this);
deps.add(dep);
}
/** Removes an input from this module. */
public void remove(CompilerInput input) {
input.setModule(null);
inputs.remove(input);
}
/** Removes all of the inputs from this module
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Can be subclassed to apply checks or
* store additional state when adding.
*/
static class ReferenceCollection implements Iterable<Reference> {
List<Reference> references = Lists.newArrayList();
@Override
public Iterator<Reference> iterator() {
return references.iterator();
}
void add(Reference reference, NodeTraversal t, Var v) {
references.add(reference);
}
/**
* Determines if the variable for this reference collection is
* "well-defined." A variable is well-defined if we can prove at
* compile-time that it's assigned a value before it's used.
*
* Notice that if this function returns false, this doesn't imply that the
* variable is used before it's assigned. It just means that we don't
* have enough information to make a definitive judgment.
*/
protected boolean isWellDefined() {
int size = references.size();
if (size == 0) {
return false;
}
// If this is a declaration that does not instantiate the variable,
// it's not well-defined.
Reference init = getInitializingReference();
if (init == null) {
return false;
}
Preconditions.checkState(references.get(0).isDeclaration());
BasicBlock initBlock = init.getBasicBlock();
for (int i = 1; i < size; i++) {
if (!initBlock.provablyExecutesBefore(
references.get(i).getBasicBlock())) {
return false;
}
}
return true;
}
/**
* Whether the variable is escaped into an inner scope.
*/
boolean isEscaped() {
Scope scope = null;
for (Reference ref : references) {
if (scope == null) {
scope = ref.scope;
} else if (scope != ref.scope) {
return true;
}
}
return false;
}
/**
* @param index The index into the references array to look for an
* assigning declaration.
*
* This is either the declaration if a value is assigned (such as
* "var a = 2", "function a()...", "... catch (a)...").
*/
private boolean isInitializingDeclarationAt(int index) {
Reference maybeInit = references.get(index);
if (maybeInit.is
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>InitializingDeclaration()) {
// This is a declaration that represents the initial value.
// Specifically, var declarations without assignments such as "var a;"
// are not.
return true;
}
return false;
}
/**
* @param index The index into the references array to look for an
* initialized assignment reference. That is, an assignment immediately
* follow a variable declaration that itself does not initialize the
* variable.
*/
private boolean isInitializingAssignmentAt(int index) {
if (index < references.size() && index > 0) {
Reference maybeDecl = references.get(index-1);
if (maybeDecl.isVarDeclaration()) {
Preconditions.checkState(!maybeDecl.isInitializingDeclaration());
Reference maybeInit = references.get(index);
if (maybeInit.isSimpleAssignmentToName()) {
return true;
}
}
}
return false;
}
/**
* @return The reference that provides the value for the variable at the
* time of the first read, if known, otherwise null.
*
* This is either the variable declaration ("var a = ...") or first
* reference following the declaration if it is an assignment.
*/
Reference getInitializingReference() {
if (isInitializingDeclarationAt(0)) {
return references.get(0);
} else if (isInitializingAssignmentAt(1)) {
return references.get(1);
}
return null;
}
/**
* Constants are allowed to be defined after their first use.
*/
Reference getInitializingReferenceForConstants() {
int size = references.size();
for (int i = 0; i < size; i++) {
if (isInitializingDeclarationAt(i) || isInitializingAssignmentAt(i)) {
return references.get(i);
}
}
return null;
}
/**
* @return Whether the variable is only assigned a value once for its
* lifetime.
*/
boolean isAssignedOnceInLifetime() {
Reference ref = getOneAndOnlyAssignment();
if (ref == null) {
return false;
}
// Make sure this assignment is not in a loop.
for (BasicBlock block = ref.getBasicBlock();
block != null; block = block.getParent()) {
if (block.isFunction) {
break;
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> report;
return this;
}
/**
* Main entry point for this phase of processing. This follows the pattern for
* JSCompiler phases.
*
* @param externsRoot The root of the externs parse tree.
* @param jsRoot The root of the input parse tree to be checked.
*/
@Override
public void process(Node externsRoot, Node jsRoot) {
Preconditions.checkNotNull(scopeCreator);
Preconditions.checkNotNull(topScope);
Node externsAndJs = jsRoot.getParent();
Preconditions.checkState(externsAndJs != null);
Preconditions.checkState(
externsRoot == null || externsAndJs.hasChild(externsRoot));
if (externsRoot != null) {
check(externsRoot, true);
}
check(jsRoot, false);
}
/** Main entry point of this phase for testing code. */
public Scope processForTesting(Node externsRoot, Node jsRoot) {
Preconditions.checkState(scopeCreator == null);
Preconditions.checkState(topScope == null);
Preconditions.checkState(jsRoot.getParent() != null);
Node externsAndJsRoot = jsRoot.getParent();
scopeCreator = new MemoizedScopeCreator(new TypedScopeCreator(compiler));
topScope = scopeCreator.createScope(externsAndJsRoot, null);
TypeInferencePass inference = new TypeInferencePass(compiler,
reverseInterpreter, topScope, scopeCreator);
inference.process(externsRoot, jsRoot);
process(externsRoot, jsRoot);
return topScope;
}
public void check(Node node, boolean externs) {
Preconditions.checkNotNull(node);
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
inExterns = externs;
t.traverseWithScope(node, topScope);
if (externs) {
inferJSDocInfo.process(node, null);
} else {
inferJSDocInfo.process(null, node);
}
}
private void checkNoTypeCheckSection(Node n, boolean enterSection) {
switch (n.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.VAR:
case Token.FUNCTION:
case Token.ASSIGN:
JSDocInfo info
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>JSDocInfo(prop);
if (docInfo != null && docInfo.isImplicitCast()) {
return true;
}
}
return false;
}
/**
* Given a constructor type and a property name, check that the property has
* the JSDoc annotation @override iff the property is declared on a
* superclass. Several checks regarding inheritance correctness are also
* performed.
*/
private void checkDeclaredPropertyInheritance(
NodeTraversal t, Node n, FunctionType ctorType, String propertyName,
JSDocInfo info, JSType propertyType) {
// If the supertype doesn't resolve correctly, we've warned about this
// already.
if (hasUnknownOrEmptySupertype(ctorType)) {
return;
}
FunctionType superClass = ctorType.getSuperClassConstructor();
boolean superClassHasProperty = superClass != null &&
superClass.getInstanceType().hasProperty(propertyName);
boolean superClassHasDeclaredProperty = superClass != null &&
superClass.getInstanceType().isPropertyTypeDeclared(propertyName);
// For interface
boolean superInterfaceHasProperty = false;
boolean superInterfaceHasDeclaredProperty = false;
if (ctorType.isInterface()) {
for (ObjectType interfaceType : ctorType.getExtendedInterfaces()) {
superInterfaceHasProperty =
superInterfaceHasProperty ||
interfaceType.hasProperty(propertyName);
superInterfaceHasDeclaredProperty =
superInterfaceHasDeclaredProperty ||
interfaceType.isPropertyTypeDeclared(propertyName);
}
}
boolean declaredOverride = info != null && info.isOverride();
boolean foundInterfaceProperty = false;
if (ctorType.isConstructor()) {
for (JSType implementedInterface :
ctorType.getAllImplementedInterfaces()) {
if (implementedInterface.isUnknownType() ||
implementedInterface.isEmptyType()) {
continue;
}
FunctionType interfaceType =
implementedInterface.toObjectType().getConstructor();
Preconditions.checkNotNull(interfaceType);
boolean interfaceHasProperty =
interfaceType.getPrototype().hasProperty(propertyName);
foundInterfaceProperty = foundInterfaceProperty ||
interfaceHasProperty;
if (reportMissingOverride.isOn()
&& !declaredOverride
&& interfaceHasProperty) {
// @override not present, but the property does override an interface
// property
compiler.report(t.makeError(n, reportMissingOverride,
HIDDEN_INTERFACE
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(
t.makeError(n, UNKNOWN_OVERRIDE,
propertyName, ctorType.getInstanceType().toString()));
}
}
/**
* Given a constructor or an interface type, find out whether the unknown
* type is a supertype of the current type.
*/
private static boolean hasUnknownOrEmptySupertype(FunctionType ctor) {
Preconditions.checkArgument(ctor.isConstructor() || ctor.isInterface());
Preconditions.checkArgument(!ctor.isUnknownType());
// The type system should notice inheritance cycles on its own
// and break the cycle.
while (true) {
ObjectType maybeSuperInstanceType =
ctor.getPrototype().getImplicitPrototype();
if (maybeSuperInstanceType == null) {
return false;
}
if (maybeSuperInstanceType.isUnknownType() ||
maybeSuperInstanceType.isEmptyType()) {
return true;
}
ctor = maybeSuperInstanceType.getConstructor();
if (ctor == null) {
return false;
}
Preconditions.checkState(ctor.isConstructor() || ctor.isInterface());
}
}
/**
* Visits an ASSIGN node for cases such as
* <pre>
* interface.property2.property = ...;
* </pre>
*/
private void visitInterfaceGetprop(NodeTraversal t, Node assign, Node object,
String property, Node lvalue, Node rvalue) {
JSType rvalueType = getJSType(rvalue);
// Only 2 values are allowed for methods:
// goog.abstractMethod
// function () {};
// or for properties, no assignment such as:
// InterfaceFoo.prototype.foobar;
String abstractMethodName =
compiler.getCodingConvention().getAbstractMethodName();
if (!rvalueType.isFunctionType()) {
// This is bad i18n style but we don't localize our compiler errors.
String abstractMethodMessage = (abstractMethodName != null)
? ", or " + abstractMethodName
: "";
compiler.report(
t.makeError(object, INVALID_INTERFACE_MEMBER_DECLARATION,
abstractMethodMessage));
}
if (assign.getLastChild().isFunction()
&& !NodeUtil.isEmptyBlock(assign.getLastChild().getLastChild())) {
compiler.report(
t.makeError(object, INTERFACE_FUNCTION_NOT
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
// time as the type checker is extended.
return getNativeType(UNKNOWN_TYPE);
} else {
return jsType;
}
}
// TODO(nicksantos): TypeCheck should never be attaching types to nodes.
// All types should be attached by TypeInference. This is not true today
// for legacy reasons. There are a number of places where TypeInference
// doesn't attach a type, as a signal to TypeCheck that it needs to check
// that node's type.
/**
* Ensure that the given node has a type. If it does not have one,
* attach the UNKNOWN_TYPE.
*/
private void ensureTyped(NodeTraversal t, Node n) {
ensureTyped(t, n, getNativeType(UNKNOWN_TYPE));
}
private void ensureTyped(NodeTraversal t, Node n, JSTypeNative type) {
ensureTyped(t, n, getNativeType(type));
}
/**
* Enforces type casts, and ensures the node is typed.
*
* A cast in the way that we use it in JSDoc annotations never
* alters the generated code and therefore never can induce any runtime
* operation. What this means is that a 'cast' is really just a compile
* time constraint on the underlying value. In the future, we may add
* support for run-time casts for compiled tests.
*
* To ensure some shred of sanity, we enforce the notion that the
* type you are casting to may only meaningfully be a narrower type
* than the underlying declared type. We also invalidate optimizations
* on bad type casts.
*
* @param t The traversal object needed to report errors.
* @param n The node getting a type assigned to it.
* @param type The type to be assigned.
*/
private void ensureTyped(NodeTraversal t, Node n, JSType type) {
// Make sure FUNCTION nodes always get function type.
Preconditions.checkState(!n.isFunction() ||
type.isFunctionType() ||
type.isUnknownType());
JSDocInfo info = n.getJSDocInfo();
if (info != null) {
if (info.hasType()) {
JSType infoType = info.getType().evaluate(t.getScope(), typeRegistry);
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
}
@Override
HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) {
return self.getHotSwapPass(compiler);
}
};
}
/**
* Creates a new compiler pass to be run.
*/
final CompilerPass create(AbstractCompiler compiler) {
Preconditions.checkState(!isCreated || !isOneTimePass,
"One-time passes cannot be run multiple times: %s", name);
isCreated = true;
return createInternal(compiler);
}
/**
* Creates a new compiler pass to be run.
*/
abstract protected CompilerPass createInternal(AbstractCompiler compiler);
/**
* Any factory whose CompilerPass has a corresponding hot-swap version should
* override this.
*
* @param compiler The compiler that can has been used to do the full compile.
*/
HotSwapCompilerPass getHotSwapPass(AbstractCompiler compiler) {
// TODO(bashir): If in future most of PassFactory's in DefaultPassConfig
// turns out to be DefaultPassConfig.HotSwapPassFactory, we should probably
// change the implementation here by the one in HotSwapPassFactory.
return null;
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> {
ret = transform(n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
}
Preconditions.checkState(ret.isString());
return ret;
}
@Override
Node processArrayLiteral(ArrayLiteral literalNode) {
if (literalNode.isDestructuring()) {
reportDestructuringAssign(literalNode);
}
Node node = newNode(Token.ARRAYLIT);
for (AstNode child : literalNode.getElements()) {
Node c = transform(child);
node.addChildToBack(c);
}
return node;
}
@Override
Node processAssignment(Assignment assignmentNode) {
Node assign = processInfixExpression(assignmentNode);
Node target = assign.getFirstChild();
if (!validAssignmentTarget(target)) {
errorReporter.error(
"invalid assignment target",
sourceName,
target.getLineno(), "", 0);
}
return assign;
}
@Override
Node processAstRoot(AstRoot rootNode) {
Node node = newNode(Token.SCRIPT);
for (com.google.javascript.rhino.head.Node child : rootNode) {
node.addChildToBack(transform((AstNode)child));
}
parseDirectives(node);
return node;
}
/**
* Parse the directives, encode them in the AST, and remove their nodes.
*
* For information on ES5 directives, see section 14.1 of
* ECMA-262, Edition 5.
*
* It would be nice if Rhino would eventually take care of this for
* us, but right now their directive-processing is a one-off.
*/
private void parseDirectives(Node node) {
// Remove all the directives, and encode them in the AST.
Set<String> directives = null;
while (isDirective(node.getFirstChild())) {
String directive = node.removeFirstChild().getFirstChild().getString();
if (directives == null) {
directives = Sets.newHashSet(directive);
} else {
directives.add(directive);
}
}
if (directives != null) {
node.setDirectives(directives);
}
}
private boolean isDirective(Node n) {
if (n == null) return false;
int n
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>isName()) {
lp.addChildToBack(paramNode);
} else {
// We expect this in ideMode or when there is an error handling
// destructuring parameter assignments which aren't supported
// (an error has already been reported).
Preconditions.checkState(
config.isIdeMode
|| paramNode.isObjectLit()
|| paramNode.isArrayLit());
}
}
node.addChildToBack(lp);
Node bodyNode = transform(functionNode.getBody());
if (!bodyNode.isBlock()) {
// When in ideMode Rhino tries to parse some constructs the compiler
// doesn't support, repair it here. see Rhino's
// Parser#parseFunctionBodyExpr.
Preconditions.checkState(config.isIdeMode);
bodyNode = IR.block();
}
parseDirectives(bodyNode);
node.addChildToBack(bodyNode);
return node;
}
@Override
Node processIfStatement(IfStatement statementNode) {
Node node = newNode(Token.IF);
node.addChildToBack(transform(statementNode.getCondition()));
node.addChildToBack(transformBlock(statementNode.getThenPart()));
if (statementNode.getElsePart() != null) {
node.addChildToBack(transformBlock(statementNode.getElsePart()));
}
return node;
}
@Override
Node processInfixExpression(InfixExpression exprNode) {
Node n = newNode(
transformTokenType(exprNode.getType()),
transform(exprNode.getLeft()),
transform(exprNode.getRight()));
n.setLineno(exprNode.getLineno());
n.setCharno(position2charno(exprNode.getAbsolutePosition()));
maybeSetLengthFrom(n, exprNode);
return n;
}
@Override
Node processKeywordLiteral(KeywordLiteral literalNode) {
return newNode(transformTokenType(literalNode.getType()));
}
@Override
Node processLabel(Label labelNode) {
return newStringNode(Token.LABEL_NAME, labelNode.getName());
}
@Override
Node processLabeledStatement(LabeledStatement statementNode) {
Node node = newNode(Token.LABEL);
Node prev = null;
Node cur = node;
for (Label label : statementNode.getLabels()) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Token.GETTER_DEF);
Preconditions.checkState(value.isFunction());
if (getFnParamNode(value).hasChildren()) {
reportGetterParam(el.getLeft());
}
} else if (el.isSetter()) {
key.setType(Token.SETTER_DEF);
Preconditions.checkState(value.isFunction());
if (!getFnParamNode(value).hasOneChild()) {
reportSetterParam(el.getLeft());
}
}
key.addChildToFront(value);
node.addChildToBack(key);
}
return node;
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
Node getFnParamNode(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.isFunction());
return fnNode.getFirstChild().getNext();
}
@Override
Node processObjectProperty(ObjectProperty propertyNode) {
return processInfixExpression(propertyNode);
}
@Override
Node processParenthesizedExpression(ParenthesizedExpression exprNode) {
Node node = transform(exprNode.getExpression());
node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
return node;
}
@Override
Node processPropertyGet(PropertyGet getNode) {
Node leftChild = transform(getNode.getTarget());
Node newNode = newNode(
Token.GETPROP, leftChild, transformAsString(getNode.getProperty()));
newNode.setLineno(leftChild.getLineno());
newNode.setCharno(leftChild.getCharno());
maybeSetLengthFrom(newNode, getNode);
return newNode;
}
@Override
Node processRegExpLiteral(RegExpLiteral literalNode) {
Node literalStringNode = newStringNode(literalNode.getValue());
// assume it's on the same line.
literalStringNode.setLineno(literalNode.getLineno());
maybeSetLengthFrom(literalStringNode, literalNode);
Node node = newNode(Token.REGEXP, literalStringNode);
String flags = literalNode.getFlags();
if (flags != null && !flags.isEmpty()) {
Node flagsNode = newStringNode(flags);
// Assume the flags are on the same line as
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/*
* Copyright 2007 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import static com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt.LINE;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.javascript.jscomp.CheckLevel;
import com.google.javascript.jscomp.SourceExcerptProvider.ExcerptFormatter;
import com.google.javascript.jscomp.SourceExcerptProvider.SourceExcerpt;
/**
* Lightweight message formatter. The format of messages this formatter
* produces is very compact and to the point.
*
*/
public class LightweightMessageFormatter extends AbstractMessageFormatter {
private SourceExcerpt excerpt;
private static final ExcerptFormatter excerptFormatter =
new LineNumberingFormatter();
/**
* A constructor for when the client doesn't care about source information.
*/
private LightweightMessageFormatter() {
super(null);
this.excerpt = LINE;
}
public LightweightMessageFormatter(SourceExcerptProvider source) {
this(source, LINE);
}
public LightweightMessageFormatter(SourceExcerptProvider source,
SourceExcerpt excerpt) {
super(source);
Preconditions.checkNotNull(source);
this.excerpt = excerpt;
}
static LightweightMessageFormatter withoutSource() {
return new LightweightMessageFormatter();
}
@Override
public String formatError(JSError error) {
return format(error, false);
}
@Override
public String formatWarning(JSError warning) {
return format(warning, true);
}
private String format(J
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>().iterator().next());
}
symbols.add(symbols.get(0));
return Joiner.on(" -> ").join(symbols);
}
public List<INPUT> getSortedList() {
return Collections.<INPUT>unmodifiableList(sortedList);
}
/**
* Gets all the dependencies of the given roots. The inputs must be returned
* in a stable order. In other words, if A comes before B, and A does not
* transitively depend on B, then A must also come before B in the returned
* list.
*/
public List<INPUT> getSortedDependenciesOf(List<INPUT> roots) {
return getDependenciesOf(roots, true);
}
/**
* Gets all the dependencies of the given roots. The inputs must be returned
* in a stable order. In other words, if A comes before B, and A does not
* transitively depend on B, then A must also come before B in the returned
* list.
*
* @param sorted If true, get them in topologically sorted order. If false,
* get them in the original order they were passed to the compiler.
*/
public List<INPUT> getDependenciesOf(List<INPUT> roots, boolean sorted) {
Preconditions.checkArgument(inputs.containsAll(roots));
Set<INPUT> included = Sets.newHashSet();
Deque<INPUT> worklist = new ArrayDeque<INPUT>(roots);
while (!worklist.isEmpty()) {
INPUT current = worklist.pop();
if (included.add(current)) {
for (String req : current.getRequires()) {
INPUT dep = provideMap.get(req);
if (dep != null) {
worklist.add(dep);
}
}
}
}
ImmutableList.Builder<INPUT> builder = ImmutableList.builder();
for (INPUT current : (sorted ? sortedList : inputs)) {
if (included.contains(current)) {
builder.add(current);
}
}
return builder.build();
}
public List<INPUT> getInputsWithoutProvides() {
return Collections.<INPUT>unmodifiableList(noProvides);
}
private static <T> List<T> topologicalStableSort(
List<T> items, Multimap<T, T> deps) {
if (items
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
this.filenamePrefix = filenamePrefix.endsWith(File.separator) ?
filenamePrefix : filenamePrefix + File.separator;
this.reportDependencies = reportDependencies;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal
.traverse(compiler, root, new ProcessCommonJsModulesCallback());
}
String guessCJSModuleName(String filename) {
return toModuleName(normalizeSourceName(filename));
}
/**
* For every file that is being processed this returns the module that
* created for it.
*/
JSModule getModule() {
return module;
}
/**
* Turns a filename into a JS identifier that is used for moduleNames in
* rewritten code. Removes leading ./, replaces / with $, removes trailing .js
* and replaces - with _. All moduleNames get a "module$" prefix.
*/
public static String toModuleName(String filename) {
return MODULE_NAME_PREFIX +
filename.replaceAll("^\\." + Pattern.quote(File.separator), "")
.replaceAll(Pattern.quote(File.separator), MODULE_NAME_SEPARATOR)
.replaceAll("\\.js$", "").replaceAll("-", "_");
}
/**
* Turn a filename into a moduleName with support for relative addressing
* with ./ and ../ based on currentFilename;
*/
public static String toModuleName(String requiredFilename,
String currentFilename) {
requiredFilename = requiredFilename.replaceAll("\\.js$", "");
currentFilename = currentFilename.replaceAll("\\.js$", "");
if (requiredFilename.startsWith("." + File.separator) ||
requiredFilename.startsWith(".." + File.separator)) {
try {
requiredFilename = (new URI(currentFilename)).resolve(new URI(requiredFilename))
.toString();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
return toModuleName(requiredFilename);
}
private String normalizeSourceName(String filename) {
if (filename.indexOf(filenamePrefix) == 0) {
filename = filename.substring(filenamePrefix.length());
}
return filename;
}
/**
* Visits require, every "script" and special module.exports assignments.
*/
private class ProcessCommonJsModulesCallback extends
AbstractPostOrderCallback {
private int scriptNodeCount = 0;
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> private Set<String> modulesWithExports = Sets.newHashSet();
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isCall() && n.getChildCount() == 2 &&
"require".equals(n.getFirstChild().getQualifiedName()) &&
n.getChildAtIndex(1).isString()) {
visitRequireCall(t, n, parent);
}
if (n.isScript()) {
scriptNodeCount++;
visitScript(t, n);
}
if (n.isGetProp() &&
"module.exports".equals(n.getQualifiedName())) {
visitModuleExports(n);
}
}
/**
* Visit require calls. Emit corresponding goog.require and rewrite require
* to be a direct reference to name of require module.
*/
private void visitRequireCall(NodeTraversal t, Node require, Node parent) {
String moduleName = toModuleName(require.getChildAtIndex(1).getString(),
normalizeSourceName(t.getSourceName()));
Node moduleRef = IR.name(moduleName).srcref(require);
parent.replaceChild(require, moduleRef);
Node script = getCurrentScriptNode(parent);
if (reportDependencies) {
t.getInput().addRequire(moduleName);
}
// Rewrite require("name").
script.addChildToFront(IR.exprResult(
IR.call(IR.getprop(IR.name("goog"), IR.string("require")),
IR.string(moduleName))).copyInformationFromForTree(require));
compiler.reportCodeChange();
}
/**
* Emit goog.provide and add suffix to all global vars to avoid conflicts
* with other modules.
*/
private void visitScript(NodeTraversal t, Node script) {
Preconditions.checkArgument(scriptNodeCount == 1,
"ProcessCommonJSModules supports only one invocation per " +
"CompilerInput / script node");
String moduleName = guessCJSModuleName(normalizeSourceName(script
.getSourceFileName()));
script.addChildToFront(IR.var(IR.name(moduleName), IR.objectlit())
.copyInformationFromForTree(script));
if (reportDependencies) {
CompilerInput ci = t.getInput();
ci.addProvide(moduleName);
JSModule m = new JSModule(moduleName);
m.add
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> having a mismatch
* in super class declaration, and only before properties on that type are
* processed.
*/
final void setImplicitPrototype(ObjectType implicitPrototype) {
checkState(!hasCachedValues());
this.implicitPrototypeFallback = implicitPrototype;
}
@Override
public String getReferenceName() {
if (className != null) {
return className;
} else if (ownerFunction != null) {
return ownerFunction.getReferenceName() + ".prototype";
} else {
return null;
}
}
@Override
public boolean hasReferenceName() {
return className != null || ownerFunction != null;
}
@Override
public boolean isSubtype(JSType that) {
if (JSType.isSubtypeHelper(this, that)) {
return true;
}
// Union types
if (that.isUnionType()) {
// The static {@code JSType.isSubtype} check already decomposed
// union types, so we don't need to check those again.
return false;
}
// record types
if (that.isRecordType()) {
return RecordType.isSubtype(this, that.toMaybeRecordType());
}
// Interfaces
// Find all the interfaces implemented by this class and compare each one
// to the interface instance.
ObjectType thatObj = that.toObjectType();
ObjectType thatCtor = thatObj == null ? null : thatObj.getConstructor();
if (thatCtor != null && thatCtor.isInterface()) {
Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces();
for (ObjectType thisInterface : thisInterfaces) {
if (thisInterface.isSubtype(that)) {
return true;
}
}
}
if (getConstructor() != null && getConstructor().isInterface()) {
for (ObjectType thisInterface : getCtorExtendedInterfaces()) {
if (thisInterface.isSubtype(that)) {
return true;
}
}
}
// other prototype based objects
if (isUnknownType() || implicitPrototypeChainIsUnknown()) {
// If unsure, say 'yes', to avoid spurious warnings.
// TODO(user): resolve the prototype chain completely in all cases,
// to avoid guessing.
return true;
}
return this.isImplicitPrototype(thatObj);
}
private boolean implicitPrototypeChainIsUnknown() {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
ObjectType p = getImplicitPrototype();
while (p != null) {
if (p.isUnknownType()) {
return true;
}
p = p.getImplicitPrototype();
}
return false;
}
@Override
public boolean hasCachedValues() {
return super.hasCachedValues();
}
/** Whether this is a built-in object. */
@Override
public boolean isNativeObjectType() {
return nativeType;
}
@Override
void setOwnerFunction(FunctionType type) {
Preconditions.checkState(ownerFunction == null || type == null);
ownerFunction = type;
}
@Override
public FunctionType getOwnerFunction() {
return ownerFunction;
}
@Override
public Iterable<ObjectType> getCtorImplementedInterfaces() {
return isFunctionPrototypeType()
? getOwnerFunction().getImplementedInterfaces()
: ImmutableList.<ObjectType>of();
}
@Override
public Iterable<ObjectType> getCtorExtendedInterfaces() {
return isFunctionPrototypeType()
? getOwnerFunction().getExtendedInterfaces()
: ImmutableList.<ObjectType>of();
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this);
ObjectType implicitPrototype = getImplicitPrototype();
if (implicitPrototype != null) {
implicitPrototypeFallback =
(ObjectType) implicitPrototype.resolve(t, scope);
}
for (Property prop : properties.values()) {
prop.setType(safeResolve(prop.getType(), t, scope));
}
return this;
}
@Override
public void matchConstraint(ObjectType constraintObj) {
// We only want to match constraints on anonymous types.
if (hasReferenceName()) {
return;
}
// Handle the case where the constraint object is a record type.
//
// param constraintObj {{prop: (number|undefined)}}
// function f(constraintObj) {}
// f({});
//
// We want to modify the object literal to match the constraint, by
// taking any each property on the record and trying to match
// properties on this object.
if (constraintObj.isRecordType()) {
for (String prop : constraintObj.getOwnPropertyNames()) {
JSType propType = constraintObj.getPropertyType(prop);
if (!isPropertyTypeDeclared(prop)) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>_LOOP_ERROR",
"Exceeded max number of optimization iterations: {0}");
public static final DiagnosticType MOTION_ITERATIONS_ERROR =
DiagnosticType.error("JSC_OPTIMIZE_LOOP_ERROR",
"Exceeded max number of code motion iterations: {0}");
private static final long COMPILER_STACK_SIZE = 1048576L;
/**
* Logger for the whole com.google.javascript.jscomp domain -
* setting configuration for this logger affects all loggers
* in other classes within the compiler.
*/
private static final Logger logger =
Logger.getLogger("com.google.javascript.jscomp");
private final PrintStream outStream;
private GlobalVarReferenceMap globalRefMap = null;
private volatile double progress = 0.0;
/**
* Creates a Compiler that reports errors and warnings to its logger.
*/
public Compiler() {
this((PrintStream) null);
}
/**
* Creates n Compiler that reports errors and warnings to an output
* stream.
*/
public Compiler(PrintStream stream) {
addChangeHandler(recentChange);
outStream = stream;
}
/**
* Creates a Compiler that uses a custom error manager.
*/
public Compiler(ErrorManager errorManager) {
this();
setErrorManager(errorManager);
}
/**
* Sets the error manager.
*
* @param errorManager the error manager, it cannot be {@code null}
*/
public void setErrorManager(ErrorManager errorManager) {
Preconditions.checkNotNull(
errorManager, "the error manager cannot be null");
this.errorManager = errorManager;
}
/**
* Creates a message formatter instance corresponding to the value of
* {@link CompilerOptions}.
*/
private MessageFormatter createMessageFormatter() {
boolean colorize = options.shouldColorizeErrorOutput();
return options.errorFormat.toFormatter(this, colorize);
}
/**
* Initialize the compiler options. Only necessary if you're not doing
* a normal compile() job.
*/
public void initOptions(CompilerOptions options) {
this.options = options;
if (errorManager == null) {
if (outStream == null) {
setErrorManager(
new LoggerErrorManager(createMessageFormatter(), logger));
} else {
Print
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>File extern, SourceFile input, CompilerOptions options) {
return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options);
}
public Result compile(
SourceFile extern, JSSourceFile[] input, CompilerOptions options) {
return compile(Lists.newArrayList(extern), Lists.newArrayList(input), options);
}
public Result compile(
JSSourceFile extern, JSModule[] modules, CompilerOptions options) {
return compileModules(
Lists.newArrayList(extern), Lists.newArrayList(modules), options);
}
/**
* Compiles a list of inputs.
*/
public Result compile(JSSourceFile[] externs,
JSSourceFile[] inputs,
CompilerOptions options) {
return compile(Lists.<SourceFile>newArrayList(externs),
Lists.<SourceFile>newArrayList(inputs),
options);
}
/**
* Compiles a list of inputs.
*/
public <T1 extends SourceFile, T2 extends SourceFile> Result compile(
List<T1> externs, List<T2> inputs, CompilerOptions options) {
// The compile method should only be called once.
Preconditions.checkState(jsRoot == null);
try {
init(externs, inputs, options);
if (hasErrors()) {
return getResult();
}
return compile();
} finally {
Tracer t = newTracer("generateReport");
errorManager.generateReport();
stopTracer(t, "generateReport");
}
}
/**
* Compiles a list of modules.
*/
public Result compile(JSSourceFile[] externs,
JSModule[] modules,
CompilerOptions options) {
return compileModules(Lists.<SourceFile>newArrayList(externs),
Lists.<JSModule>newArrayList(modules),
options);
}
/**
* Compiles a list of modules.
*/
public <T extends SourceFile> Result compileModules(List<T> externs,
List<JSModule> modules, CompilerOptions options) {
// The compile method should only be called once.
Preconditions.checkState(jsRoot == null);
try {
initModules(externs, modules, options);
if (hasErrors()) {
return getResult();
}
return compile();
} finally {
Tracer t = newTracer("generateReport");
error
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> // own PassConfig object.
Preconditions.checkNotNull(passes);
if (this.passes != null) {
throw new IllegalStateException("this.passes has already been assigned");
}
this.passes = passes;
}
/**
* Carry out any special checks or procedures that need to be done before
* proceeding with rest of the compilation process.
*
* @return true, to continue with compilation
*/
boolean precheck() {
return true;
}
public void check() {
runCustomPasses(CustomPassExecutionTime.BEFORE_CHECKS);
// We are currently only interested in check-passes for progress reporting
// as it is used for IDEs, that's why the maximum progress is set to 1.0.
PhaseOptimizer phaseOptimizer = new PhaseOptimizer(this, tracker,
new PhaseOptimizer.ProgressRange(getProgress(), 1.0));
if (options.devMode == DevMode.EVERY_PASS) {
phaseOptimizer.setSanityCheck(sanityCheck);
}
phaseOptimizer.consume(getPassConfig().getChecks());
phaseOptimizer.process(externsRoot, jsRoot);
if (hasErrors()) {
return;
}
// TODO(nicksantos): clean this up. The flow here is too hard to follow.
if (options.nameAnonymousFunctionsOnly) {
return;
}
if (options.removeTryCatchFinally) {
removeTryCatchFinally();
}
if (options.getTweakProcessing().shouldStrip() ||
!options.stripTypes.isEmpty() ||
!options.stripNameSuffixes.isEmpty() ||
!options.stripTypePrefixes.isEmpty() ||
!options.stripNamePrefixes.isEmpty()) {
stripCode(options.stripTypes, options.stripNameSuffixes,
options.stripTypePrefixes, options.stripNamePrefixes);
}
runCustomPasses(CustomPassExecutionTime.BEFORE_OPTIMIZATIONS);
}
private void externExports() {
logger.fine("Creating extern file for exports");
startPass("externExports");
ExternExportsPass pass = new ExternExportsPass(this);
process(pass);
externExports = pass.getGeneratedExterns();
endPass();
}
@Override
void process(CompilerPass p) {
p.process(externsRoot,
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> jsRoot);
}
private final PassFactory sanityCheck =
new PassFactory("sanityCheck", false) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new SanityCheck(compiler);
}
};
private void maybeSanityCheck() {
if (options.devMode == DevMode.EVERY_PASS) {
runSanityCheck();
}
}
private void runSanityCheck() {
sanityCheck.create(this).process(externsRoot, jsRoot);
}
/**
* Removes try/catch/finally statements for easier debugging.
*/
void removeTryCatchFinally() {
logger.fine("Remove try/catch/finally");
startPass("removeTryCatchFinally");
RemoveTryCatch r = new RemoveTryCatch(this);
process(r);
endPass();
}
/**
* Strips code for smaller compiled code. This is useful for removing debug
* statements to prevent leaking them publicly.
*/
void stripCode(Set<String> stripTypes, Set<String> stripNameSuffixes,
Set<String> stripTypePrefixes, Set<String> stripNamePrefixes) {
logger.fine("Strip code");
startPass("stripCode");
StripCode r = new StripCode(this, stripTypes, stripNameSuffixes,
stripTypePrefixes, stripNamePrefixes);
if (options.getTweakProcessing().shouldStrip()) {
r.enableTweakStripping();
}
process(r);
endPass();
}
/**
* Runs custom passes that are designated to run at a particular time.
*/
private void runCustomPasses(CustomPassExecutionTime executionTime) {
if (options.customPasses != null) {
Tracer t = newTracer("runCustomPasses");
try {
for (CompilerPass p : options.customPasses.get(executionTime)) {
process(p);
}
} finally {
stopTracer(t, "runCustomPasses");
}
}
}
private Tracer currentTracer = null;
private String currentPassName = null;
/**
* Marks the beginning of a pass.
*/
void startPass(String passName) {
Preconditions.checkState(currentTracer == null);
currentPassName = passName;
current
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Tracer = newTracer(passName);
}
/**
* Marks the end of a pass.
*/
void endPass() {
Preconditions.checkState(currentTracer != null,
"Tracer should not be null at the end of a pass.");
stopTracer(currentTracer, currentPassName);
String passToCheck = currentPassName;
currentPassName = null;
currentTracer = null;
maybeSanityCheck();
}
/**
* Returns a new tracer for the given pass name.
*/
Tracer newTracer(String passName) {
String comment = passName
+ (recentChange.hasCodeChanged() ? " on recently changed AST" : "");
if (options.tracer.isOn()) {
tracker.recordPassStart(passName);
}
return new Tracer("Compiler", comment);
}
void stopTracer(Tracer t, String passName) {
long result = t.stop();
if (options.tracer.isOn()) {
tracker.recordPassStop(passName, result);
}
}
/**
* Returns the result of the compilation.
*/
public Result getResult() {
PassConfig.State state = getPassConfig().getIntermediateState();
return new Result(getErrors(), getWarnings(), debugLog.toString(),
state.variableMap, state.propertyMap,
state.anonymousFunctionNameMap, state.stringMap, functionInformationMap,
sourceMap, externExports, state.cssNames, state.idGeneratorMap);
}
/**
* Returns an array constructed from errors + temporary warnings.
*/
public JSError[] getMessages() {
return getErrors();
}
/**
* Returns the array of errors (never null).
*/
public JSError[] getErrors() {
return errorManager.getErrors();
}
/**
* Returns the array of warnings (never null).
*/
public JSError[] getWarnings() {
return errorManager.getWarnings();
}
@Override
public Node getRoot() {
return externAndJsRoot;
}
/**
* Creates a new id for making unique names.
*/
private int nextUniqueNameId() {
return uniqueNameId++;
}
/**
* Resets the unique name id counter
*/
@VisibleForTesting
void resetUniqueNameId() {
uniqueNameId
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = 0;
}
@Override
Supplier<String> getUniqueNameIdSupplier() {
final Compiler self = this;
return new Supplier<String>() {
@Override
public String get() {
return String.valueOf(self.nextUniqueNameId());
}
};
}
@Override
boolean areNodesEqualForInlining(Node n1, Node n2) {
if (options.ambiguateProperties ||
options.disambiguateProperties) {
// The type based optimizations require that type information is preserved
// during other optimizations.
return n1.isEquivalentToTyped(n2);
} else {
return n1.isEquivalentTo(n2);
}
}
//------------------------------------------------------------------------
// Inputs
//------------------------------------------------------------------------
// TODO(nicksantos): Decide which parts of these belong in an AbstractCompiler
// interface, and which ones should always be injected.
@Override
public CompilerInput getInput(InputId id) {
return inputsById.get(id);
}
/**
* Removes an input file from AST.
* @param id The id of the input to be removed.
*/
protected void removeExternInput(InputId id) {
CompilerInput input = getInput(id);
if (input == null) {
return;
}
Preconditions.checkState(input.isExtern(), "Not an extern input: %s", input.getName());
inputsById.remove(id);
externs.remove(input);
Node root = input.getAstRoot(this);
if (root != null) {
root.detachFromParent();
}
}
@Override
public CompilerInput newExternInput(String name) {
SourceAst ast = new SyntheticAst(name);
if (inputsById.containsKey(ast.getInputId())) {
throw new IllegalArgumentException("Conflicting externs name: " + name);
}
CompilerInput input = new CompilerInput(ast, true);
putCompilerInput(input.getInputId(), input);
externsRoot.addChildToFront(ast.getAstRoot(this));
externs.add(0, input);
return input;
}
private CompilerInput putCompilerInput(InputId id, CompilerInput input) {
input.setCompiler(this);
return inputsById.put(id
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, input);
}
/** Add a source input dynamically. Intended for incremental compilation. */
void addIncrementalSourceAst(JsAst ast) {
InputId id = ast.getInputId();
Preconditions.checkState(getInput(id) == null, "Duplicate input %s", id.getIdName());
putCompilerInput(id, new CompilerInput(ast));
}
/**
* Replace a source input dynamically. Intended for incremental
* re-compilation.
*
* If the new source input doesn't parse, then keep the old input
* in the AST and return false.
*
* @return Whether the new AST was attached successfully.
*/
boolean replaceIncrementalSourceAst(JsAst ast) {
CompilerInput oldInput = getInput(ast.getInputId());
Preconditions.checkNotNull(oldInput, "No input to replace: %s", ast.getInputId().getIdName());
Node newRoot = ast.getAstRoot(this);
if (newRoot == null) {
return false;
}
Node oldRoot = oldInput.getAstRoot(this);
if (oldRoot != null) {
oldRoot.getParent().replaceChild(oldRoot, newRoot);
} else {
getRoot().getLastChild().addChildToBack(newRoot);
}
CompilerInput newInput = new CompilerInput(ast);
putCompilerInput(ast.getInputId(), newInput);
JSModule module = oldInput.getModule();
if (module != null) {
module.addAfter(newInput, oldInput);
module.remove(oldInput);
}
// Verify the input id is set properly.
Preconditions.checkState(
newInput.getInputId().equals(oldInput.getInputId()));
InputId inputIdOnAst = newInput.getAstRoot(this).getInputId();
Preconditions.checkState(newInput.getInputId().equals(inputIdOnAst));
inputs.remove(oldInput);
return true;
}
/**
* Add a new source input dynamically. Intended for incremental compilation.
* <p>
* If the new source input doesn't parse, it will not be added, and a false
* will be returned.
*
* @param ast the JS Source to add.
* @return true if the source was added successfully, false otherwise.
* @
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>throws IllegalStateException if an input for this ast already exists.
*/
boolean addNewSourceAst(JsAst ast) {
CompilerInput oldInput = getInput(ast.getInputId());
if (oldInput != null) {
throw new IllegalStateException(
"Input already exists: " + ast.getInputId().getIdName());
}
Node newRoot = ast.getAstRoot(this);
if (newRoot == null) {
return false;
}
getRoot().getLastChild().addChildToBack(newRoot);
CompilerInput newInput = new CompilerInput(ast);
// TODO(tylerg): handle this for multiple modules at some point.
if (moduleGraph == null && !modules.isEmpty()) {
// singleton module
modules.get(0).add(newInput);
}
putCompilerInput(ast.getInputId(), newInput);
return true;
}
@Override
JSModuleGraph getModuleGraph() {
return moduleGraph;
}
/**
* Gets a module graph. This will always return a module graph, even
* in the degenerate case when there's only one module.
*/
JSModuleGraph getDegenerateModuleGraph() {
return moduleGraph == null ? new JSModuleGraph(modules) : moduleGraph;
}
@Override
public JSTypeRegistry getTypeRegistry() {
if (typeRegistry == null) {
typeRegistry = new JSTypeRegistry(oldErrorReporter, options.looseTypes);
}
return typeRegistry;
}
@Override
public MemoizedScopeCreator getTypedScopeCreator() {
return getPassConfig().getTypedScopeCreator();
}
@SuppressWarnings("unchecked")
DefaultPassConfig ensureDefaultPassConfig() {
PassConfig passes = getPassConfig().getBasePassConfig();
Preconditions.checkState(passes instanceof DefaultPassConfig,
"PassConfigs must eventually delegate to the DefaultPassConfig");
return (DefaultPassConfig) passes;
}
public SymbolTable buildKnownSymbolTable() {
SymbolTable symbolTable = new SymbolTable(getTypeRegistry());
MemoizedScopeCreator typedScopeCreator = getTypedScopeCreator();
if (typedScopeCreator != null) {
symbolTable.addScopes(typedScopeCreator.getAllMemoizedScopes());
symbolTable.addSymbolsFrom(typedScopeCreator);
} else {
symbolTable.findScopes(this, extern
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) {
return "";
}
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
if (scriptNode == null) {
throw new IllegalArgumentException(
"Bad module: " + module.getName());
}
toSource(cb, i, scriptNode);
}
return cb.toString();
}
});
}
/**
* Converts the parse tree for each input in a module back to JS code.
*/
public String[] toSourceArray(final JSModule module) {
return runInCompilerThread(new Callable<String[]>() {
@Override
public String[] call() throws Exception {
List<CompilerInput> inputs = module.getInputs();
int numInputs = inputs.size();
if (numInputs == 0) {
return new String[0];
}
String[] sources = new String[numInputs];
CodeBuilder cb = new CodeBuilder();
for (int i = 0; i < numInputs; i++) {
Node scriptNode = inputs.get(i).getAstRoot(Compiler.this);
if (scriptNode == null) {
throw new IllegalArgumentException(
"Bad module input: " + inputs.get(i).getName());
}
cb.reset();
toSource(cb, i, scriptNode);
sources[i] = cb.toString();
}
return sources;
}
});
}
/**
* Writes out JS code from a root node. If printing input delimiters, this
* method will attach a comment to the start of the text indicating which
* input the output derived from. If there were any preserve annotations
* within the root's source, they will also be printed in a block comment
* at the beginning of the output.
*/
public void toSource(final CodeBuilder cb,
final int inputSeqNum,
final Node root) {
runInCompilerThread(new Callable<Void>() {
@Override
public Void call() throws Exception {
if (options.printInputDelimiter) {
if ((cb.getLength() > 0) && !cb.endsWith("\n")) {
cb.append("\n"); // Make sure that the label starts on a new line
}
Preconditions
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.checkState(root.isScript());
String delimiter = options.inputDelimiter;
String inputName = root.getInputId().getIdName();
String sourceName = root.getSourceFileName();
Preconditions.checkState(sourceName != null);
Preconditions.checkState(!sourceName.isEmpty());
delimiter = delimiter
.replaceAll("%name%", Matcher.quoteReplacement(inputName))
.replaceAll("%num%", String.valueOf(inputSeqNum));
cb.append(delimiter)
.append("\n");
}
if (root.getJSDocInfo() != null &&
root.getJSDocInfo().getLicense() != null) {
cb.append("/*\n")
.append(root.getJSDocInfo().getLicense())
.append("*/\n");
}
// If there is a valid source map, then indicate to it that the current
// root node's mappings are offset by the given string builder buffer.
if (options.sourceMapOutputPath != null) {
sourceMap.setStartingPosition(
cb.getLineIndex(), cb.getColumnIndex());
}
// if LanguageMode is ECMASCRIPT5_STRICT, only print 'use strict'
// for the first input file
String code = toSource(root, sourceMap, inputSeqNum == 0);
if (!code.isEmpty()) {
cb.append(code);
// In order to avoid parse ambiguity when files are concatenated
// together, all files should end in a semi-colon. Do a quick
// heuristic check if there's an obvious semi-colon already there.
int length = code.length();
char lastChar = code.charAt(length - 1);
char secondLastChar = length >= 2 ?
code.charAt(length - 2) : '\0';
boolean hasSemiColon = lastChar == ';' ||
(lastChar == '\n' && secondLastChar == ';');
if (!hasSemiColon) {
cb.append(";");
}
}
return null;
}
});
}
/**
* Generates JavaScript source code for an AST, doesn't generate source
* map info.
*/
@Override
String toSource(Node n) {
initCompilerOptionsIfTesting();
return toSource(n, null, true);
}
/**
*
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> @Override
public boolean acceptEcmaScript5() {
switch (options.getLanguageIn()) {
case ECMASCRIPT5:
case ECMASCRIPT5_STRICT:
return true;
}
return false;
}
public LanguageMode languageMode() {
return options.getLanguageIn();
}
@Override
public boolean acceptConstKeyword() {
return options.acceptConstKeyword;
}
@Override
Config getParserConfig() {
if (parserConfig == null) {
Config.LanguageMode mode;
switch (options.getLanguageIn()) {
case ECMASCRIPT3:
mode = Config.LanguageMode.ECMASCRIPT3;
break;
case ECMASCRIPT5:
mode = Config.LanguageMode.ECMASCRIPT5;
break;
case ECMASCRIPT5_STRICT:
mode = Config.LanguageMode.ECMASCRIPT5_STRICT;
break;
default:
throw new IllegalStateException("unexpected language mode");
}
parserConfig = ParserRunner.createConfig(
isIdeMode(),
mode,
acceptConstKeyword(),
options.extraAnnotationNames);
}
return parserConfig;
}
@Override
public boolean isTypeCheckingEnabled() {
return options.checkTypes;
}
//------------------------------------------------------------------------
// Error reporting
//------------------------------------------------------------------------
/**
* The warning classes that are available from the command-line, and
* are suppressible by the {@code @suppress} annotation.
*/
protected DiagnosticGroups getDiagnosticGroups() {
return new DiagnosticGroups();
}
@Override
public void report(JSError error) {
CheckLevel level = error.getDefaultLevel();
if (warningsGuard != null) {
CheckLevel newLevel = warningsGuard.level(error);
if (newLevel != null) {
level = newLevel;
}
}
if (level.isOn()) {
if (getOptions().errorHandler != null) {
getOptions().errorHandler.report(level, error);
}
errorManager.report(level, error);
}
}
@Override
public CheckLevel getErrorLevel(JSError error) {
Preconditions.checkNotNull(options);
return warningsGuard.level(error);
}
/**
* Report an internal error.
*/
@Override
void throwInternalError(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>modules;
passes = createPassConfigInternal();
getPassConfig().setIntermediateState(state.passConfigState);
typeRegistry = state.typeRegistry;
setLifeCycleStage(state.lifeCycleStage);
injectedLibraries.clear();
injectedLibraries.putAll(state.injectedLibraries);
}
@VisibleForTesting
List<CompilerInput> getInputsForTesting() {
return inputs;
}
@VisibleForTesting
List<CompilerInput> getExternsForTesting() {
return externs;
}
@Override
boolean hasRegExpGlobalReferences() {
return hasRegExpGlobalReferences;
}
@Override
void setHasRegExpGlobalReferences(boolean references) {
hasRegExpGlobalReferences = references;
}
@Override
void updateGlobalVarReferences(Map<Var, ReferenceCollection> refMapPatch,
Node collectionRoot) {
Preconditions.checkState(collectionRoot.isScript()
|| collectionRoot.isBlock());
if (globalRefMap == null) {
globalRefMap = new GlobalVarReferenceMap(getInputsInOrder(),
getExternsInOrder());
}
globalRefMap.updateGlobalVarReferences(refMapPatch, collectionRoot);
}
@Override
GlobalVarReferenceMap getGlobalVarReferences() {
return globalRefMap;
}
@Override
CompilerInput getSynthesizedExternsInput() {
if (synthesizedExternsInput == null) {
synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS);
}
return synthesizedExternsInput;
}
@Override
public double getProgress() {
return progress;
}
@Override
void setProgress(double newProgress) {
if (newProgress > 1.0) {
progress = 1.0;
} else if (newProgress < 0.0) {
progress = 0.0;
} else {
progress = newProgress;
}
}
/**
* Replaces one file in a hot-swap mode. The given JsAst should be made
* from a new version of a file that already was present in the last compile
* call. If the file is new, this will silently ignored.
*
* @param ast the ast of the file that is being replaced
*/
public void replaceScript(JsAst
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> ast) {
CompilerInput input = this.getInput(ast.getInputId());
if (!replaceIncrementalSourceAst(ast)) {
return;
}
Node originalRoot = input.getAstRoot(this);
processNewScript(ast, originalRoot);
}
/**
* Adds a new Script AST to the compile state. If a script for the same file
* already exists the script will not be added, instead a call to
* #replaceScript should be used.
*
* @param ast the ast of the new file
*/
public void addNewScript(JsAst ast) {
if (!addNewSourceAst(ast)) {
return;
}
Node emptyScript = new Node(Token.SCRIPT);
InputId inputId = ast.getInputId();
emptyScript.setInputId(inputId);
emptyScript.setStaticSourceFile(
SourceFile.fromCode(inputId.getIdName(), ""));
processNewScript(ast, emptyScript);
}
private void processNewScript(JsAst ast, Node originalRoot) {
Node js = ast.getAstRoot(this);
Preconditions.checkNotNull(js);
runHotSwap(originalRoot, js, this.getCleanupPassConfig());
// NOTE: If hot swap passes that use GlobalNamespace are added, we will need
// to revisit this approach to clearing GlobalNamespaces
runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks);
this.getTypeRegistry().clearNamedTypes();
this.removeSyntheticVarsInput();
runHotSwap(originalRoot, js, this.ensureDefaultPassConfig());
}
/**
* Execute the passes from a PassConfig instance over a single replaced file.
*/
private void runHotSwap(
Node originalRoot, Node js, PassConfig passConfig) {
for (PassFactory passFactory : passConfig.getChecks()) {
runHotSwapPass(originalRoot, js, passFactory);
}
}
private void runHotSwapPass(
Node originalRoot, Node js, PassFactory passFactory) {
HotSwapCompilerPass pass = passFactory.getHotSwapPass(this);
if (pass != null) {
logger.info("Performing HotSwap for pass " + passFactory.getName());
pass.hotSwapScript(js, originalRoot);
}
}
private PassConfig
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> }
@Override
public SourceFile getSourceFile() {
return sourceFile;
}
@Override
public void setSourceFile(SourceFile file) {
Preconditions.checkState(fileName.equals(file.getName()));
sourceFile = file;
}
private void parse(AbstractCompiler compiler) {
try {
logger_.fine("Parsing: " + sourceFile.getName());
root = ParserRunner.parse(sourceFile, sourceFile.getCode(),
compiler.getParserConfig(),
compiler.getDefaultErrorReporter(),
logger_);
} catch (IOException e) {
compiler.report(
JSError.make(AbstractCompiler.READ_ERROR, sourceFile.getName()));
}
if (root == null || compiler.hasHaltingErrors()) {
// There was a parse error or IOException, so use a dummy block.
root = IR.script();
} else {
compiler.prepareAst(root);
}
// Set the source name so that the compiler passes can track
// the source file and module.
root.setStaticSourceFile(sourceFile);
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Name, int lineno, int charno) {
super(registry, registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE));
Preconditions.checkNotNull(reference);
this.reference = reference;
this.sourceName = sourceName;
this.lineno = lineno;
this.charno = charno;
}
@Override
boolean defineProperty(String propertyName, JSType type,
boolean inferred, Node propertyNode) {
if (!isResolved()) {
// If this is an unresolved object type, we need to save all its
// properties and define them when it is resolved.
if (propertyContinuations == null) {
propertyContinuations = Lists.newArrayList();
}
propertyContinuations.add(
new PropertyContinuation(
propertyName, type, inferred, propertyNode));
return true;
} else {
return super.defineProperty(
propertyName, type, inferred, propertyNode);
}
}
private void finishPropertyContinuations() {
ObjectType referencedObjType = getReferencedObjTypeInternal();
if (referencedObjType != null && !referencedObjType.isUnknownType()) {
if (propertyContinuations != null) {
for (PropertyContinuation c : propertyContinuations) {
c.commit(this);
}
}
}
propertyContinuations = null;
}
/** Returns the type to which this refers (which is unknown if unresolved). */
public JSType getReferencedType() {
return getReferencedTypeInternal();
}
@Override
public String getReferenceName() {
return reference;
}
@Override
String toStringHelper(boolean forAnnotations) {
return reference;
}
@Override
public boolean hasReferenceName() {
return true;
}
@Override
boolean isNamedType() {
return true;
}
@Override
public boolean isNominalType() {
return true;
}
/**
* Two named types are equivalent if they are the same {@code
* ObjectType} object. This is complicated by the fact that isEquivalent
* is sometimes called before we have a chance to resolve the type
* names.
*
* @return {@code true} iff {@code that} == {@code this} or {@code that}
* is a {@link NamedType} whose reference is the same as ours,
* or {@code that} is the type we reference
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Compiler compiler, RedeclarationHandler redeclarationHandler) {
this.compiler = compiler;
this.redeclarationHandler = redeclarationHandler;
}
@Override
public Scope createScope(Node n, Scope parent) {
inputId = null;
if (parent == null) {
scope = new Scope(n, compiler);
} else {
scope = new Scope(parent, n);
}
scanRoot(n, parent);
inputId = null;
Scope returnedScope = scope;
scope = null;
return returnedScope;
}
private void scanRoot(Node n, Scope parent) {
if (n.isFunction()) {
if (inputId == null) {
inputId = NodeUtil.getInputId(n);
// TODO(johnlenz): inputId maybe null if the FUNCTION node is detached
// from the AST.
// Is it meaningful to build a scope for detached FUNCTION node?
}
final Node fnNameNode = n.getFirstChild();
final Node args = fnNameNode.getNext();
final Node body = args.getNext();
// Bleed the function name into the scope, if it hasn't
// been declared in the outer scope.
String fnName = fnNameNode.getString();
if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) {
declareVar(fnNameNode);
}
// Args: Declare function variables
Preconditions.checkState(args.isParamList());
for (Node a = args.getFirstChild(); a != null;
a = a.getNext()) {
Preconditions.checkState(a.isName());
declareVar(a);
}
// Body
scanVars(body, n);
} else {
// It's the global block
Preconditions.checkState(scope.getParent() == null);
scanVars(n, null);
}
}
/**
* Scans and gather variables declarations under a Node
*/
private void scanVars(Node n, Node parent) {
switch (n.getType()) {
case Token.VAR:
// Declare all variables. e.g. var x = 1, y, z;
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
declareVar(child);
child = next;
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> return;
case Token.FUNCTION:
if (NodeUtil.isFunctionExpression(n)) {
return;
}
String fnName = n.getFirstChild().getString();
if (fnName.isEmpty()) {
// This is invalid, but allow it so the checks can catch it.
return;
}
declareVar(n.getFirstChild());
return; // should not examine function's children
case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 2);
Preconditions.checkState(n.getFirstChild().isName());
// the first child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext();
declareVar(var);
scanVars(block, n);
return; // only one child to scan
case Token.SCRIPT:
inputId = n.getInputId();
Preconditions.checkNotNull(inputId);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child, n);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name, Node n, CompilerInput input);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
@Override
public void onRedeclaration(
Scope s, String name, Node n, CompilerInput input) {
Node parent = n.getParent();
// Don't allow multiple variables to be declared at the top-level scope
if (scope.isGlobal()) {
Scope.Var origVar = scope.getVar(name);
Node origParent = origVar.getParentNode();
if (origParent.isCatch() &&
parent.isCatch()) {
// Okay, both are 'catch(x)' variables.
return;
}
boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar);
if (!
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>allowDupe) {
compiler.report(
JSError.make(NodeUtil.getSourceName(n), n,
VAR_MULTIPLY_DECLARED_ERROR,
name,
(origVar.input != null
? origVar.input.getName()
: "??")));
}
} else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) {
// Disallow shadowing "arguments" as we can't handle with our current
// scope modeling.
compiler.report(
JSError.make(NodeUtil.getSourceName(n), n,
VAR_ARGUMENTS_SHADOWED_ERROR));
}
}
}
/**
* Declares a variable.
*
* @param n The node corresponding to the variable name.
*/
private void declareVar(Node n) {
Preconditions.checkState(n.isName());
CompilerInput input = compiler.getInput(inputId);
String name = n.getString();
if (scope.isDeclared(name, false)
|| (scope.isLocal() && name.equals(ARGUMENTS))) {
redeclarationHandler.onRedeclaration(
scope, name, n, input);
} else {
scope.declare(name, n, null, input);
}
}
/**
* @param n The name node to check.
* @param origVar The associated Var.
* @return Whether duplicated declarations warnings should be suppressed
* for the given node.
*/
static boolean hasDuplicateDeclarationSuppression(Node n, Scope.Var origVar) {
Preconditions.checkState(n.isName());
Node parent = n.getParent();
Node origParent = origVar.getParentNode();
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
info = parent.getJSDocInfo();
}
if (info != null && info.getSuppressions().contains("duplicate")) {
return true;
}
info = origVar.nameNode.getJSDocInfo();
if (info == null) {
info = origParent.getJSDocInfo();
}
return (info != null && info.getSuppressions().contains("duplicate"));
}
/**
* Generates an untyped global scope from the root of
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> output lattice element type.
*/
static class FlowState<L extends LatticeElement> implements Annotation {
private L in;
private L out;
/**
* Private constructor. No other classes should create new states.
*
* @param inState Input.
* @param outState Output.
*/
private FlowState(L inState, L outState) {
Preconditions.checkNotNull(inState);
Preconditions.checkNotNull(outState);
this.in = inState;
this.out = outState;
}
L getIn() {
return in;
}
void setIn(L in) {
Preconditions.checkNotNull(in);
this.in = in;
}
L getOut() {
return out;
}
void setOut(L out) {
Preconditions.checkNotNull(out);
this.out = out;
}
@Override
public String toString() {
return String.format("IN: %s OUT: %s", in, out);
}
@Override
public int hashCode() {
return Objects.hashCode(in, out);
}
}
/**
* The exception to be thrown if the analysis has been running for a long
* number of iterations. Chances are the analysis is not monotonic, a
* fixed-point cannot be found and it is currently stuck in an infinite loop.
*/
static class MaxIterationsExceededException extends RuntimeException {
private static final long serialVersionUID = 1L;
MaxIterationsExceededException(String msg) {
super(msg);
}
}
abstract static class BranchedForwardDataFlowAnalysis
<N, L extends LatticeElement> extends DataFlowAnalysis<N, L> {
@Override
protected void initialize() {
orderedWorkSet.clear();
for (DiGraphNode<N, Branch> node : getCfg().getDirectedGraphNodes()) {
int outEdgeCount = getCfg().getOutEdges(node.getValue()).size();
List<L> outLattices = Lists.newArrayList();
for (int i = 0; i < outEdgeCount; i++) {
outLattices.add(createInitialEstimateLattice());
}
node.setAnnotation(new BranchedFlowState<L>(
createInitialEstimateLattice(), outLattices));
if (node != getCfg().
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>getImplicitReturn()) {
orderedWorkSet.add(node);
}
}
}
BranchedForwardDataFlowAnalysis(ControlFlowGraph<N> targetCfg,
JoinOp<L> joinOp) {
super(targetCfg, joinOp);
}
/**
* Returns the lattice element at the exit point. Needs to be overridden
* because we use a BranchedFlowState instead of a FlowState; ugh.
*/
@Override
L getExitLatticeElement() {
DiGraphNode<N, Branch> node = getCfg().getImplicitReturn();
BranchedFlowState<L> state = node.getAnnotation();
return state.getIn();
}
@Override
final boolean isForward() {
return true;
}
/**
* The branched flow function maps a single lattice to a list of output
* lattices.
*
* <p>Each outgoing edge of a node will have a corresponding output lattice
* in the ordered returned by
* {@link com.google.javascript.jscomp.graph.DiGraph#getOutEdges(Object)}
* in the returned list.
*
* @return A list of output values depending on the edge's branch type.
*/
abstract List<L> branchedFlowThrough(N node, L input);
@Override
protected final boolean flow(DiGraphNode<N, Branch> node) {
BranchedFlowState<L> state = node.getAnnotation();
List<L> outBefore = state.out;
state.out = branchedFlowThrough(node.getValue(), state.in);
Preconditions.checkState(outBefore.size() == state.out.size());
for (int i = 0; i < outBefore.size(); i++) {
if (!outBefore.get(i).equals(state.out.get(i))) {
return true;
}
}
return false;
}
@Override
protected void joinInputs(DiGraphNode<N, Branch> node) {
BranchedFlowState<L> state = node.getAnnotation();
List<DiGraphNode<N, Branch>> predNodes =
getCfg().getDirectedPredNodes(node);
List<L> values = new ArrayList<L>(predNodes.size());
for (DiGraph
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node<N, Branch> predNode : predNodes) {
BranchedFlowState<L> predNodeState = predNode.getAnnotation();
L in = predNodeState.out.get(
getCfg().getDirectedSuccNodes(predNode).indexOf(node));
values.add(in);
}
if (getCfg().getEntry() == node) {
state.setIn(createEntryLattice());
} else if (!values.isEmpty()) {
state.setIn(joinOp.apply(values));
}
}
}
/**
* The in and out states of a node.
*
* @param <L> Input and output lattice element type.
*/
static class BranchedFlowState<L extends LatticeElement>
implements Annotation {
private L in;
private List<L> out;
/**
* Private constructor. No other classes should create new states.
*
* @param inState Input.
* @param outState Output.
*/
private BranchedFlowState(L inState, List<L> outState) {
Preconditions.checkNotNull(inState);
Preconditions.checkNotNull(outState);
this.in = inState;
this.out = outState;
}
L getIn() {
return in;
}
void setIn(L in) {
Preconditions.checkNotNull(in);
this.in = in;
}
List<L> getOut() {
return out;
}
void setOut(List<L> out) {
Preconditions.checkNotNull(out);
for (L item : out) {
Preconditions.checkNotNull(item);
}
this.out = out;
}
@Override
public String toString() {
return String.format("IN: %s OUT: %s", in, out);
}
@Override
public int hashCode() {
return Objects.hashCode(in, out);
}
}
/**
* Compute set of escaped variables. When a variable is escaped in a
* dataflow analysis, it can be reference outside of the code that we are
* analyzing. A variable is escaped if any of the following is true:
*
* <p><ol>
* <li>It is defined as the exception name in CATCH clause so it became a
* variable local not
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>PropNode} does not
* correspond to a valid JS message node
*/
private void extractMessageFromProperty(
Builder builder, Node getPropNode, Node assignNode)
throws MalformedException {
Node callNode = getPropNode.getNext();
maybeInitMetaDataFromJsDoc(builder, assignNode);
extractFromCallNode(builder, callNode);
}
/**
* Initializes the meta data in a JsMessage by examining the nodes just before
* and after a message VAR node.
*
* @param builder the message builder whose meta data will be initialized
* @param varNode the message VAR node
* @param parentOfVarNode {@code varNode}'s parent node
*/
private void maybeInitMetaDataFromJsDocOrHelpVar(
Builder builder, Node varNode, @Nullable Node parentOfVarNode)
throws MalformedException {
// First check description in @desc
if (maybeInitMetaDataFromJsDoc(builder, varNode)) {
return;
}
// Check the preceding node for meta data
if ((parentOfVarNode != null) &&
maybeInitMetaDataFromHelpVar(builder,
parentOfVarNode.getChildBefore(varNode))) {
return;
}
// Check the subsequent node for meta data
maybeInitMetaDataFromHelpVar(builder, varNode.getNext());
}
/**
* Initializes the meta data in a JsMessage by examining a node just before or
* after a message VAR node.
*
* @param builder the message builder whose meta data will be initialized
* @param sibling a node adjacent to the message VAR node
* @return true iff message has corresponding description variable
*/
private boolean maybeInitMetaDataFromHelpVar(Builder builder,
@Nullable Node sibling) throws MalformedException {
if ((sibling != null) && (sibling.isVar())) {
Node nameNode = sibling.getFirstChild();
String name = nameNode.getString();
if (name.equals(builder.getKey() + DESC_SUFFIX)) {
Node valueNode = nameNode.getFirstChild();
String desc = extractStringFromStringExprNode(valueNode);
if (desc.startsWith(HIDDEN_DESC_PREFIX)) {
builder.setDesc(desc.substring(HIDDEN_DESC_PREFIX.length()).trim());
builder.setIsHidden(true);
} else {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> a reference to
* an unregistered placeholder
*/
private void parseMessageTextNode(Builder builder, Node node)
throws MalformedException {
String value = extractStringFromStringExprNode(node);
while(true) {
int phBegin = value.indexOf(PH_JS_PREFIX);
if (phBegin < 0) {
// Just a string literal
builder.appendStringPart(value);
return;
} else {
if (phBegin > 0) {
// A string literal followed by a placeholder
builder.appendStringPart(value.substring(0, phBegin));
}
// A placeholder. Find where it ends
int phEnd = value.indexOf(PH_JS_SUFFIX, phBegin);
if (phEnd < 0) {
throw new MalformedException(
"Placeholder incorrectly formatted in: " + builder.getKey(),
node);
}
String phName = value.substring(phBegin + PH_JS_PREFIX.length(),
phEnd);
builder.appendPlaceholderReference(phName);
int nextPos = phEnd + PH_JS_SUFFIX.length();
if (nextPos < value.length()) {
// Iterate on the rest of the message value
value = value.substring(nextPos);
} else {
// The message is parsed
return;
}
}
}
}
/**
* Processes found JS message. Several examples of "standard" processing
* routines are:
* <ol>
* <li>extract all JS messages
* <li>replace JS messages with localized versions for some specific language
* <li>check that messages have correct syntax and present in localization
* bundle
* </ol>
*
* @param message the found message
* @param definition the definition of the object and usually contains all
* additional message information like message node/parent's node
*/
abstract void processJsMessage(JsMessage message,
JsMessageDefinition definition);
/**
* Returns whether the given JS identifier is a valid JS message name.
*/
boolean isMessageName(String identifier, boolean isNewStyleMessage) {
return identifier.startsWith(MSG_PREFIX) &&
(style == JsMessage.Style.CLOSURE || isNewStyleMessage ||
!identifier.endsWith(DESC_SUFFIX));
}
/**
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> thousands and thousands of
* type invalidations, which makes reporting all of the errors useless.
* However, certain properties are worth specifically guarding because of the
* large amount of code that can be removed as dead code. This list contains
* the properties (eg: "toString") that we care about; if any of these
* properties is invalidated it causes an error.
*/
private final Map<String, CheckLevel> propertiesToErrorFor;
private class Property {
/** The name of the property. */
final String name;
/** All types on which the field exists, grouped together if related. */
private UnionFind<T> types;
/**
* A set of types for which renaming this field should be skipped. This
* list is first filled by fields defined in the externs file.
*/
Set<T> typesToSkip = Sets.newHashSet();
/**
* If true, do not rename any instance of this field, as it has been
* referenced from an unknown type.
*/
boolean skipRenaming;
/** Set of nodes for this field that need renaming. */
Set<Node> renameNodes = Sets.newHashSet();
/**
* Map from node to the highest type in the prototype chain containing the
* field for that node. In the case of a union, the type is the highest type
* of one of the types in the union.
*/
final Map<Node, T> rootTypes = Maps.newHashMap();
Property(String name) {
this.name = name;
}
/** Returns the types on which this field is referenced. */
UnionFind<T> getTypes() {
if (types == null) {
types = new StandardUnionFind<T>();
}
return types;
}
/**
* Record that this property is referenced from this type.
* @return true if the type was recorded for this property, else false,
* which would happen if the type was invalidating.
*/
boolean addType(T type, T top, T relatedType) {
checkState(!skipRenaming, "Attempt to record skipped property: %s", name);
if (typeSystem.isInvalidatingType(top)) {
invalidate();
return false;
} else {
if (typeSystem.isTypeToSkip(top)) {
addTypeToSkip
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(top);
}
if (relatedType == null) {
getTypes().add(top);
} else {
getTypes().union(top, relatedType);
}
typeSystem.recordInterfaces(type, top, this);
return true;
}
}
/** Records the given type as one to skip for this property. */
void addTypeToSkip(T type) {
for (T skipType : typeSystem.getTypesToSkipForType(type)) {
typesToSkip.add(skipType);
getTypes().union(skipType, type);
}
}
/** Invalidates any types related to invalid types. */
void expandTypesToSkip() {
// If we are not going to rename any properties, then we do not need to
// update the list of invalid types, as they are all invalid.
if (shouldRename()) {
int count = 0;
while (true) {
// It should usually only take one time through this do-while.
checkState(++count < 10, "Stuck in loop expanding types to skip.");
// Make sure that the representative type for each type to skip is
// marked as being skipped.
Set<T> rootTypesToSkip = Sets.newHashSet();
for (T subType : typesToSkip) {
rootTypesToSkip.add(types.find(subType));
}
typesToSkip.addAll(rootTypesToSkip);
Set<T> newTypesToSkip = Sets.newHashSet();
Set<T> allTypes = types.elements();
int originalTypesSize = allTypes.size();
for (T subType : allTypes) {
if (!typesToSkip.contains(subType)
&& typesToSkip.contains(types.find(subType))) {
newTypesToSkip.add(subType);
}
}
for (T newType : newTypesToSkip) {
addTypeToSkip(newType);
}
// If there were not any new types added, we are done here.
if (types.elements().size() == originalTypesSize) {
break;
}
}
}
}
/** Returns true if any instance of this property should be renamed. */
boolean shouldRename() {
return !skipRenaming && types != null
&& types
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, String> buildPropNames(UnionFind<T> types, String name) {
Map<T, String> names = Maps.newHashMap();
for (Set<T> set : types.allEquivalenceClasses()) {
checkState(!set.isEmpty());
String typeName = null;
for (T type : set) {
if (typeName == null || type.toString().compareTo(typeName) < 0) {
typeName = type.toString();
}
}
String newName;
if ("{...}".equals(typeName)) {
newName = name;
} else {
newName = typeName.replaceAll("[^\\w$]", "_") + "$" + name;
}
for (T type : set) {
names.put(type, newName);
}
}
return names;
}
/** Returns a map from field name to types for which it will be renamed. */
Multimap<String, Collection<T>> getRenamedTypesForTesting() {
Multimap<String, Collection<T>> ret = HashMultimap.create();
for (Map.Entry<String, Property> entry: properties.entrySet()) {
Property prop = entry.getValue();
if (!prop.skipRenaming) {
for (Collection<T> c : prop.getTypes().allEquivalenceClasses()) {
if (!c.isEmpty() && !prop.typesToSkip.contains(c.iterator().next())) {
ret.put(entry.getKey(), c);
}
}
}
}
return ret;
}
/** Interface for providing the type information needed by this pass. */
private interface TypeSystem<T> {
// TODO(user): add a getUniqueName(T type) method that is guaranteed
// to be unique, performant and human-readable.
/** Returns the top-most scope used by the type system (if any). */
StaticScope<T> getRootScope();
/** Returns the new scope started at the given function node. */
StaticScope<T> getFunctionScope(Node node);
/**
* Returns the type of the given node.
* @param prop Only types with this property need to be returned. In general
* with type tightening, this will require no special processing, but in
* the case of an unknown JSType, we might need to add in the native
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
public JSTypeSystem(AbstractCompiler compiler) {
registry = compiler.getTypeRegistry();
invalidatingTypes = Sets.newHashSet(
registry.getNativeType(JSTypeNative.ALL_TYPE),
registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE),
registry.getNativeType(JSTypeNative.NO_TYPE),
registry.getNativeType(JSTypeNative.FUNCTION_PROTOTYPE),
registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
registry.getNativeType(JSTypeNative.OBJECT_PROTOTYPE),
registry.getNativeType(JSTypeNative.TOP_LEVEL_PROTOTYPE),
registry.getNativeType(JSTypeNative.UNKNOWN_TYPE));
}
@Override public void addInvalidatingType(JSType type) {
checkState(!type.isUnionType());
invalidatingTypes.add(type);
}
@Override public StaticScope<JSType> getRootScope() { return null; }
@Override public StaticScope<JSType> getFunctionScope(Node node) {
return null;
}
@Override public JSType getType(
StaticScope<JSType> scope, Node node, String prop) {
if (node.getJSType() == null) {
return registry.getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
return node.getJSType();
}
@Override public boolean isInvalidatingType(JSType type) {
if (type == null || invalidatingTypes.contains(type) ||
type.isUnknownType() /* unresolved types */) {
return true;
}
ObjectType objType = ObjectType.cast(type);
return objType != null && !objType.hasReferenceName();
}
@Override public ImmutableSet<JSType> getTypesToSkipForType(JSType type) {
type = type.restrictByNotNullOrUndefined();
if (type.isUnionType()) {
Set<JSType> types = Sets.newHashSet(type);
for (JSType alt : type.toMaybeUnionType().getAlternates()) {
types.addAll(getTypesToSkipForTypeNonUnion(type));
}
return ImmutableSet.copyOf(types);
} else if (type.isEnumElementType()) {
return getTypesToSkipForType(
type
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> (constructor != null) {
for (ObjectType itype : constructor.getImplementedInterfaces()) {
JSType top = getTypeWithProperty(p.name, itype);
if (top != null) {
p.addType(itype, top, relatedType);
} else {
recordInterfaces(itype, relatedType, p);
}
// If this interface invalidated this property, return now.
if (p.skipRenaming) return;
}
if (constructor.isInterface() || constructor.isConstructor()) {
constructor = constructor.getSuperClassConstructor();
} else {
constructor = null;
}
}
}
}
}
/** Implementation of TypeSystem using concrete types. */
private static class ConcreteTypeSystem implements TypeSystem<ConcreteType> {
private final TightenTypes tt;
private int nextUniqueId;
private CodingConvention codingConvention;
private final Set<JSType> invalidatingTypes = Sets.newHashSet();
// An array of native types that are not tracked by type tightening, and
// thus need to be added in if an unknown type is encountered.
private static final JSTypeNative [] nativeTypes = new JSTypeNative[] {
JSTypeNative.BOOLEAN_OBJECT_TYPE,
JSTypeNative.NUMBER_OBJECT_TYPE,
JSTypeNative.STRING_OBJECT_TYPE
};
public ConcreteTypeSystem(TightenTypes tt, CodingConvention convention) {
this.tt = tt;
this.codingConvention = convention;
}
@Override public void addInvalidatingType(JSType type) {
checkState(!type.isUnionType());
invalidatingTypes.add(type);
}
@Override public StaticScope<ConcreteType> getRootScope() {
return tt.getTopScope();
}
@Override public StaticScope<ConcreteType> getFunctionScope(Node decl) {
ConcreteFunctionType func = tt.getConcreteFunction(decl);
return (func != null) ?
func.getScope() : (StaticScope<ConcreteType>) null;
}
@Override
public ConcreteType getType(
StaticScope<ConcreteType> scope, Node node, String prop) {
if (scope != null) {
ConcreteType c = tt.inferConcreteType(
(TightenTypes.ConcreteScope) scope, node);
return maybeAddAuto
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, or function literals.
JSDocInfo jsdoc = NodeUtil.getBestJSDocInfo(node);
if (jsdoc == null && !node.isFunction()) {
return "";
}
JSType type = node.getJSType();
if (type == null) {
return "";
} else if (type.isFunctionType()) {
return getFunctionAnnotation(node);
} else if (type.isEnumType()) {
return "/** @enum {" +
type.toMaybeEnumType().getElementsType().toAnnotationString() +
"} */\n";
} else if (!type.isUnknownType()
&& !type.isEmptyType()
&& !type.isVoidType()
&& !type.isFunctionPrototypeType()) {
return "/** @type {" + node.getJSType().toAnnotationString() + "} */\n";
} else {
return "";
}
}
/**
* @param fnNode A node for a function for which to generate a type annotation
*/
private String getFunctionAnnotation(Node fnNode) {
Preconditions.checkState(fnNode.isFunction());
StringBuilder sb = new StringBuilder("/**\n");
JSType type = fnNode.getJSType();
if (type == null || type.isUnknownType()) {
return "";
}
FunctionType funType = type.toMaybeFunctionType();
// We need to use the child nodes of the function as the nodes for the
// parameters of the function type do not have the real parameter names.
// FUNCTION
// NAME
// LP
// NAME param1
// NAME param2
if (fnNode != null) {
Node paramNode = NodeUtil.getFunctionParameters(fnNode).getFirstChild();
// Param types
for (Node n : funType.getParameters()) {
// Bail out if the paramNode is not there.
if (paramNode == null) {
break;
}
sb.append(" * ");
appendAnnotation(sb, "param", getParameterNodeJSDocType(n));
sb.append(" ")
.append(paramNode.getString())
.append("\n");
paramNode = paramNode.getNext();
}
}
// Return type
JSType retType = funType.getReturnType();
if (retType != null && !
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> we model scopes but handles some
// additional cases that are not handled by the current Scope object.
// Specifically, a Scope currently has only two concepts of scope (global,
// and function local). But there are in reality a couple of additional
// case to worry about:
// catch expressions
// function expressions names
// Both belong to a scope by themselves.
private Deque<Renamer> nameStack = new ArrayDeque<Renamer>();
private final Renamer rootRenamer;
MakeDeclaredNamesUnique() {
this(new ContextualRenamer());
}
MakeDeclaredNamesUnique(Renamer renamer) {
this.rootRenamer = renamer;
}
static CompilerPass getContextualRenameInverter(AbstractCompiler compiler) {
return new ContextualRenameInverter(compiler);
}
@Override
public void enterScope(NodeTraversal t) {
Node declarationRoot = t.getScopeRoot();
Renamer renamer;
if (nameStack.isEmpty()) {
// If the contextual renamer is being used, the starting context can not
// be a function.
Preconditions.checkState(
!declarationRoot.isFunction() ||
!(rootRenamer instanceof ContextualRenamer));
Preconditions.checkState(t.inGlobalScope());
renamer = rootRenamer;
} else {
renamer = nameStack.peek().forChildScope();
}
if (!declarationRoot.isFunction()) {
// Add the block declarations
findDeclaredNames(declarationRoot, null, renamer);
}
nameStack.push(renamer);
}
@Override
public void exitScope(NodeTraversal t) {
if (!t.inGlobalScope()) {
nameStack.pop();
}
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.FUNCTION:
{
// Add recursive function name, if needed.
// NOTE: "enterScope" is called after we need to pick up this name.
Renamer renamer = nameStack.peek().forChildScope();
// If needed, add the function recursive name.
String name = n.getFirstChild().getString();
if (name != null && !name.isEmpty() && parent != null
&&
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>containsSeparator(name) && !getOrginalName(name).isEmpty()) {
String newName = findReplacementName(name);
referencedNames.remove(name);
// Adding a reference to the new name to prevent either the parent
// scopes or the current scope renaming another var to this new name.
referencedNames.add(newName);
List<Node> references = nameMap.get(name);
Preconditions.checkState(references != null);
for (Node n : references) {
Preconditions.checkState(n.isName());
n.setString(newName);
}
compiler.reportCodeChange();
nameMap.remove(name);
}
}
/**
* Find a name usable in the local scope.
*/
private String findReplacementName(String name) {
String original = getOrginalName(name);
String newName = original;
int i = 0;
while (!isValidName(newName)) {
newName = original +
ContextualRenamer.UNIQUE_ID_SEPARATOR + String.valueOf(i++);
}
return newName;
}
/**
* @return Whether the name is valid to use in the local scope.
*/
private boolean isValidName(String name) {
if (TokenStream.isJSIdentifier(name) &&
!referencedNames.contains(name) &&
!name.equals(ARGUMENTS)) {
return true;
}
return false;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
if (t.inGlobalScope()) {
return;
}
if (NodeUtil.isReferenceName(node)) {
String name = node.getString();
// Add all referenced names to the set so it is possible to check for
// conflicts.
referencedNames.add(name);
// Store only references to candidate names in the node map.
if (containsSeparator(name)) {
addCandidateNameReference(name, node);
}
}
}
private void addCandidateNameReference(String name, Node n) {
List<Node> nodes = nameMap.get(name);
if (null == nodes) {
nodes = Lists.newLinkedList();
nameMap.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, 0, 1);
}
private int incrementNameCount(String name) {
return nameUsage.add(name, 1);
}
@Override
public boolean stripConstIfReplaced() {
return false;
}
}
/**
* Rename every declared name to be unique. Typically this would be used
* when injecting code to insure that names do not conflict with existing
* names.
*
* Used by the FunctionInjector
* @see FunctionInjector
*/
static class InlineRenamer implements Renamer {
private final Map<String, String> declarations = Maps.newHashMap();
private final Supplier<String> uniqueIdSupplier;
private final String idPrefix;
private final boolean removeConstness;
InlineRenamer(
Supplier<String> uniqueIdSupplier,
String idPrefix,
boolean removeConstness) {
this.uniqueIdSupplier = uniqueIdSupplier;
// To ensure that the id does not conflict with the id from the
// ContextualRenamer some prefix is needed.
Preconditions.checkArgument(!idPrefix.isEmpty());
this.idPrefix = idPrefix;
this.removeConstness = removeConstness;
}
@Override
public void addDeclaredName(String name) {
Preconditions.checkState(!name.equals(ARGUMENTS));
if (!declarations.containsKey(name)) {
declarations.put(name, getUniqueName(name));
}
}
private String getUniqueName(String name) {
if (name.isEmpty()) {
return name;
}
if (name.indexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR) != -1) {
name = name.substring(
0, name.lastIndexOf(ContextualRenamer.UNIQUE_ID_SEPARATOR));
}
// By using the same separator the id will be stripped if it isn't
// needed when variable renaming is turned off.
return name + ContextualRenamer.UNIQUE_ID_SEPARATOR
+ idPrefix + uniqueIdSupplier.get();
}
@Override
public String getReplacementName(String oldName) {
return declarations.get(oldName);
}
@Override
public Renamer forChildScope() {
return new InlineRenamer(uniqueIdSupplier, idPrefix, removeConstness);
}
@Override
public boolean stripConstIfReplaced()
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> where
* possible.
*/
private void maybeCollapseIntoForStatements(Node n, Node parent) {
// Only SCRIPT, BLOCK, and LABELs can have FORs that can be collapsed into.
// LABELs are not supported here.
if (parent == null || !NodeUtil.isStatementBlock(parent)) {
return;
}
// Is the current node something that can be in a for loop initializer?
if (!n.isExprResult() && !n.isVar()) {
return;
}
// Is the next statement a valid FOR?
Node nextSibling = n.getNext();
if (nextSibling == null) {
return;
} else if (NodeUtil.isForIn(nextSibling)) {
Node forNode = nextSibling;
Node forVar = forNode.getFirstChild();
if (forVar.isName()
&& n.isVar() && n.hasOneChild()) {
Node name = n.getFirstChild();
if (!name.hasChildren()
&& forVar.getString().equals(name.getString())) {
// OK, the names match, and the var declaration does not have an
// initializer. Move it into the loop.
parent.removeChild(n);
forNode.replaceChild(forVar, n);
compiler.reportCodeChange();
}
}
} else if (nextSibling.isFor()
&& nextSibling.getFirstChild().isEmpty()) {
// Does the current node contain an in operator? If so, embedding
// the expression in a for loop can cause some JavaScript parsers (such
// as the PlayStation 3's browser based on Access's NetFront
// browser) to fail to parse the code.
// See bug 1778863 for details.
if (NodeUtil.containsType(n, Token.IN)) {
return;
}
// Move the current node into the FOR loop initializer.
Node forNode = nextSibling;
Node oldInitializer = forNode.getFirstChild();
parent.removeChild(n);
Node newInitializer;
if (n.isVar()) {
newInitializer = n;
} else {
// Extract the expression from EXPR_RESULT node.
Preconditions.checkState(n.hasOneChild());
newInitializer = n.getFirstChild();
n.removeChild(newInitializer);
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> isConstant = isPropertyDeclaredConstant(objectType, propertyName);
// Check whether constant properties are reassigned
if (isConstant) {
if (isDelete) {
compiler.report(
t.makeError(getprop, CONST_PROPERTY_DELETED, propertyName));
return;
}
ObjectType oType = objectType;
while (oType != null) {
if (oType.hasReferenceName()) {
if (initializedConstantProperties.containsEntry(
oType.getReferenceName(), propertyName)) {
compiler.report(
t.makeError(getprop, CONST_PROPERTY_REASSIGNED_VALUE,
propertyName));
break;
}
}
oType = oType.getImplicitPrototype();
}
Preconditions.checkState(objectType.hasReferenceName());
initializedConstantProperties.put(objectType.getReferenceName(),
propertyName);
// Add the prototype when we're looking at an instance object
if (objectType.isInstanceType()) {
ObjectType prototype = objectType.getImplicitPrototype();
if (prototype != null) {
if (prototype.hasProperty(propertyName)
&& prototype.hasReferenceName()) {
initializedConstantProperties.put(prototype.getReferenceName(),
propertyName);
}
}
}
}
}
/**
* Determines whether the given property is visible in the current context.
* @param t The current traversal.
* @param getprop The getprop node.
*/
private void checkPropertyVisibility(NodeTraversal t,
Node getprop, Node parent) {
ObjectType objectType =
ObjectType.cast(dereference(getprop.getFirstChild().getJSType()));
String propertyName = getprop.getLastChild().getString();
if (objectType != null) {
// Is this a normal property access, or are we trying to override
// an existing property?
boolean isOverride = parent.getJSDocInfo() != null &&
parent.isAssign() &&
parent.getFirstChild() == getprop;
// Find the lowest property defined on a class with visibility
// information.
if (isOverride) {
objectType = objectType.getImplicitPrototype();
}
JSDocInfo docInfo = null;
for (; objectType != null;
objectType = objectType.getImplicitPrototype()) {
docInfo = objectType.getOwnPropertyJSDocInfo(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Code) {
sharedCallbacks.add(new CheckAccidentalSemicolon(CheckLevel.WARNING));
}
if (options.enables(DiagnosticGroups.GLOBAL_THIS)) {
sharedCallbacks.add(new CheckGlobalThis(compiler));
}
if (options.enables(DiagnosticGroups.DEBUGGER_STATEMENT_PRESENT)) {
sharedCallbacks.add(new CheckDebuggerStatement(compiler));
}
return combineChecks(compiler, sharedCallbacks);
}
};
/** Verify that all the passes are one-time passes. */
private void assertAllOneTimePasses(List<PassFactory> passes) {
for (PassFactory pass : passes) {
Preconditions.checkState(pass.isOneTimePass());
}
}
/** Verify that all the passes are multi-run passes. */
private void assertAllLoopablePasses(List<PassFactory> passes) {
for (PassFactory pass : passes) {
Preconditions.checkState(!pass.isOneTimePass());
}
}
/** Checks for validity of the control structures. */
final HotSwapPassFactory checkControlStructures =
new HotSwapPassFactory("checkControlStructures", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new ControlStructureCheck(compiler);
}
};
/** Checks that all constructed classes are goog.require()d. */
final HotSwapPassFactory checkRequires =
new HotSwapPassFactory("checkRequires", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new CheckRequiresForConstructors(compiler, options.checkRequires);
}
};
/** Makes sure @constructor is paired with goog.provides(). */
final HotSwapPassFactory checkProvides =
new HotSwapPassFactory("checkProvides", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new CheckProvides(compiler, options.checkProvides);
}
};
private static final DiagnosticType GENERATE_EXPORTS_ERROR =
DiagnosticType.error(
"JSC_GENERATE_EXPORTS_ERROR",
"Exports can only be generated if export symbol/property " +
"functions are set.");
/** Generates exports for @export annotations. */
final PassFactory generateExports =
new PassFactory("generateExports",
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>getExportedVariableNames();
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
pass.hotSwapScript(scriptRoot, originalRoot);
}
};
}
};
/** Expand jQuery Primitives and Aliases pass. */
final PassFactory jqueryAliases =
new PassFactory("jqueryAliases", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ExpandJqueryAliases(compiler);
}
};
/**
* The default i18n pass.
* A lot of the options are not configurable, because ReplaceMessages
* has a lot of legacy logic.
*/
final PassFactory replaceMessages =
new PassFactory("replaceMessages", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new ReplaceMessages(compiler,
options.messageBundle,
/* warn about message dupes */
true,
/* allow messages with goog.getMsg */
JsMessage.Style.getFromParams(true, false),
/* if we can't find a translation, don't worry about it. */
false);
}
};
/** Applies aliases and inlines goog.scope. */
final HotSwapPassFactory closureGoogScopeAliases =
new HotSwapPassFactory("processGoogScopeAliases", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
maybeInitializePreprocessorSymbolTable(compiler);
return new ScopedAliases(
compiler,
preprocessorSymbolTable,
options.getAliasTransformationHandler());
}
};
/** Checks that CSS class names are wrapped in goog.getCssName */
final PassFactory closureCheckGetCssName =
new PassFactory("checkMissingGetCssName", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
String blacklist = options.checkMissingGetCssNameBlacklist;
Preconditions.checkState(blacklist != null && !blacklist.isEmpty(),
"Not checking use of goog.getCssName because of empty blacklist.");
return new CheckMissingGetCssName(
compiler, options.checkMissingGetCssNameLevel, blacklist);
}
};
/**
* Processes goog.getCssName. The cssRenamingMap is used to lookup
* replacement values for the classnames. If null, the raw class
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>());
}
};
/** Checks that all variables are defined. */
final HotSwapPassFactory checkVars =
new HotSwapPassFactory("checkVars", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new VarCheck(compiler);
}
};
/** Checks for RegExp references. */
final PassFactory checkRegExp =
new PassFactory("checkRegExp", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
final CheckRegExp pass = new CheckRegExp(compiler);
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
pass.process(externs, root);
compiler.setHasRegExpGlobalReferences(
pass.isGlobalRegExpPropertiesUsed());
}
};
}
};
/** Checks that references to variables look reasonable. */
final HotSwapPassFactory checkVariableReferences =
new HotSwapPassFactory("checkVariableReferences", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new VariableReferenceCheck(
compiler, options.aggressiveVarCheck);
}
};
/** Pre-process goog.testing.ObjectPropertyString. */
final PassFactory objectPropertyStringPreprocess =
new PassFactory("ObjectPropertyStringPreprocess", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler) {
return new ObjectPropertyStringPreprocess(compiler);
}
};
/** Creates a typed scope and adds types to the type registry. */
final HotSwapPassFactory resolveTypes =
new HotSwapPassFactory("resolveTypes", false) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new GlobalTypeResolver(compiler);
}
};
/** Runs type inference. */
final HotSwapPassFactory inferTypes =
new HotSwapPassFactory("inferTypes", false) {
@Override
protected HotSwapCompilerPass createInternal(final AbstractCompiler
compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
makeTypeInference(compiler).process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) {
makeTypeInference(compiler).inferTypes(scriptRoot);
}
};
}
};
final HotSwapPassFactory inferJsDocInfo =
new HotSwapPassFactory("inferJsDocInfo", false) {
@Override
protected HotSwapCompilerPass createInternal(
final AbstractCompiler compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
makeInferJsDocInfo(compiler).process(externs, root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeInferJsDocInfo(compiler).hotSwapScript(scriptRoot, originalRoot);
}
};
}
};
/** Checks type usage */
final HotSwapPassFactory checkTypes =
new HotSwapPassFactory("checkTypes", false) {
@Override
protected HotSwapCompilerPass createInternal(final AbstractCompiler
compiler) {
return new HotSwapCompilerPass() {
@Override
public void process(Node externs, Node root) {
Preconditions.checkNotNull(topScope);
Preconditions.checkNotNull(getTypedScopeCreator());
TypeCheck check = makeTypeCheck(compiler);
check.process(externs, root);
compiler.getErrorManager().setTypedPercent(check.getTypedPercent());
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
makeTypeCheck(compiler).check(scriptRoot, false);
}
};
}
};
/**
* Checks possible execution paths of the program for problems: missing return
* statements and dead code.
*/
final HotSwapPassFactory checkControlFlow =
new HotSwapPassFactory("checkControlFlow", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
List<Callback> callbacks = Lists.newArrayList();
if (options.checkUnreachableCode.isOn()) {
callbacks.add(
new CheckUnreachableCode(compiler, options.checkUnreachableCode));
}
if (options.checkMissingReturn.isOn() && options.checkTypes) {
callbacks.add(
new CheckMissingReturn(compiler, options.checkMissingReturn));
}
return combineChecks(compiler, callbacks);
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> }
};
/** Checks access controls. Depends on type-inference. */
final HotSwapPassFactory checkAccessControls =
new HotSwapPassFactory("checkAccessControls", true) {
@Override
protected HotSwapCompilerPass createInternal(AbstractCompiler compiler) {
return new CheckAccessControls(compiler);
}
};
/** Executes the given callbacks with a {@link CombinedCompilerPass}. */
private static HotSwapCompilerPass combineChecks(AbstractCompiler compiler,
List<Callback> callbacks) {
Preconditions.checkArgument(callbacks.size() > 0);
Callback[] array = callbacks.toArray(new Callback[callbacks.size()]);
return new CombinedCompilerPass(compiler, array);
}
/** A compiler pass that resolves types in the global scope. */
class GlobalTypeResolver implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
GlobalTypeResolver(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
if (topScope == null) {
regenerateGlobalTypedScope(compiler, root.getParent());
} else {
compiler.getTypeRegistry().resolveTypesInScope(topScope);
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
patchGlobalTypedScope(compiler, scriptRoot);
}
}
/** Checks global name usage. */
final PassFactory checkGlobalNames =
new PassFactory("checkGlobalNames", true) {
@Override
protected CompilerPass createInternal(final AbstractCompiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node jsRoot) {
// Create a global namespace for analysis by check passes.
// Note that this class does all heavy computation lazily,
// so it's OK to create it here.
namespaceForChecks = new GlobalNamespace(compiler, externs, jsRoot);
new CheckGlobalNames(compiler, options.checkGlobalNamesLevel)
.injectNamespace(namespaceForChecks).process(externs, jsRoot);
}
};
}
};
/** Checks that the code is ES5 or Caja compliant. */
final PassFactory checkStrictMode =
new PassFactory("checkStrictMode", true) {
@Override
protected CompilerPass createInternal(AbstractCompiler compiler)
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> literal cast provides a mechanism to cast object literals to
* other types without a warning.
*/
static class ObjectLiteralCast {
/** Type to cast to. */
final String typeName;
/** Object to cast. */
final Node objectNode;
/** Error message */
final DiagnosticType diagnosticType;
ObjectLiteralCast(String typeName, Node objectNode,
DiagnosticType diagnosticType) {
this.typeName = typeName;
this.objectNode = objectNode;
this.diagnosticType = diagnosticType;
}
}
/**
* A function that will throw an exception when either:
* -One or more of its parameters evaluate to false.
* -One or more of its parameters are not of a certain type.
*/
public class AssertionFunctionSpec {
protected final String functionName;
protected final JSTypeNative assertedType;
public AssertionFunctionSpec(String functionName) {
this(functionName, null);
}
public AssertionFunctionSpec(String functionName,
JSTypeNative assertedType) {
this.functionName = functionName;
this.assertedType = assertedType;
}
/** Returns the name of the function. */
public String getFunctionName() {
return functionName;
}
/**
* Returns the parameter of the assertion function that is being checked.
* @param firstParam The first parameter of the function call.
*/
public Node getAssertedParam(Node firstParam) {
return firstParam;
}
/**
* Returns the type for a type assertion, or null if the function asserts
* that the node must not be null or undefined.
*/
public JSType getAssertedType(Node call, JSTypeRegistry registry) {
return assertedType != null ? registry.getNativeType(assertedType) : null;
}
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>JSType that) {
UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
for (JSType element : alternates) {
TypePair p = element.getTypesUnderShallowInequality(that);
if (p.typeA != null) {
thisRestricted.addAlternate(p.typeA);
}
if (p.typeB != null) {
thatRestricted.addAlternate(p.typeB);
}
}
return new TypePair(
thisRestricted.build(),
thatRestricted.build());
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseUnionType(this);
}
@Override
JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
setResolvedTypeInternal(this); // for circularly defined types.
boolean changed = false;
ImmutableList.Builder<JSType> resolvedTypes = ImmutableList.builder();
for (JSType alternate : alternates) {
JSType newAlternate = alternate.resolve(t, scope);
changed |= (alternate != newAlternate);
resolvedTypes.add(alternate);
}
if (changed) {
Collection<JSType> newAlternates = resolvedTypes.build();
Preconditions.checkState(
newAlternates.hashCode() == this.hashcode);
alternates = newAlternates;
}
return this;
}
@Override
public String toDebugHashCodeString() {
List<String> hashCodes = Lists.newArrayList();
for (JSType a : alternates) {
hashCodes.add(a.toDebugHashCodeString());
}
return "{(" + Joiner.on(",").join(hashCodes) + ")}";
}
@Override
public boolean setValidator(Predicate<JSType> validator) {
for (JSType a : alternates) {
a.setValidator(validator);
}
return true;
}
@Override
public JSType collapseUnion() {
JSType currentValue = null;
ObjectType currentCommonSuper = null;
for (JSType a : alternates) {
if (a.isUnknownType()) {
return getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
ObjectType obj = a.toObjectType
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
continue retry;
case EXPOSE:
if (!jsdocBuilder.recordExpose()) {
parser.addParserWarning("msg.jsdoc.expose",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case EXTERNS:
if (!jsdocBuilder.recordExterns()) {
parser.addParserWarning("msg.jsdoc.externs",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case JAVA_DISPATCH:
if (!jsdocBuilder.recordJavaDispatch()) {
parser.addParserWarning("msg.jsdoc.javadispatch",
stream.getLineno(), stream.getCharno());
}
token = eatTokensUntilEOL();
continue retry;
case EXTENDS:
case IMPLEMENTS:
skipEOLs();
token = next();
lineno = stream.getLineno();
charno = stream.getCharno();
boolean matchingRc = false;
if (token == JsDocToken.LC) {
token = next();
matchingRc = true;
}
if (token == JsDocToken.STRING) {
Node typeNode = parseAndRecordTypeNameNode(
token, lineno, charno, matchingRc);
lineno = stream.getLineno();
charno = stream.getCharno();
typeNode = wrapNode(Token.BANG, typeNode);
if (typeNode != null && !matchingRc) {
typeNode.putBooleanProp(Node.BRACELESS_TYPE, true);
}
type = createJSTypeExpression(typeNode);
if (annotation == Annotation.EXTENDS) {
// record the extended type, check later
extendedTypes.add(new ExtendedTypeInfo(
type, stream.getLineno(), stream.getCharno()));
} else {
Preconditions.checkState(
annotation == Annotation.IMPLEMENTS);
if (!jsdocBuilder.recordImplementedInterface(type)) {
parser.addTypeWarning("msg.jsdoc.implements.duplicate",
lineno, charno);
}
}
token = next();
if (matchingRc) {
if (token != JsDocToken.RC) {
parser.addTypeWarning("msg.jsdoc.missing.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> The type expression found or null if none.
*/
private Node parseAndRecordTypeNameNode(JsDocToken token, int lineno,
int startCharno, boolean matchingLC) {
return parseAndRecordTypeNode(token, lineno, startCharno, matchingLC, true);
}
/**
* Looks for a type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* Parameter type expressions are special for two reasons:
* <ol>
* <li>They must begin with '{', to distinguish type names from param names.
* <li>They may end in '=', to denote optionality.
* </ol>
*
* @param token The current token.
* @return The type expression found or null if none.
*/
private Node parseAndRecordParamTypeNode(JsDocToken token) {
Preconditions.checkArgument(token == JsDocToken.LC);
int lineno = stream.getLineno();
int startCharno = stream.getCharno();
Node typeNode = parseParamTypeExpressionAnnotation(token);
if (typeNode != null) {
int endLineno = stream.getLineno();
int endCharno = stream.getCharno();
jsdocBuilder.markTypeNode(typeNode, lineno, startCharno,
endLineno, endCharno, true);
}
return typeNode;
}
/**
* Looks for a parameter type expression at the current token and if found,
* returns it. Note that this method consumes input.
*
* @param token The current token.
* @param lineno The line of the type expression.
* @param startCharno The starting character position of the type expression.
* @param matchingLC Whether the type expression starts with a "{".
* @param onlyParseSimpleNames If true, only simple type names are parsed
* (via a call to parseTypeNameAnnotation instead of
* parseTypeExpressionAnnotation).
* @return The type expression found or null if none.
*/
private Node parseAndRecordTypeNode(JsDocToken token, int lineno,
int startCharno,
boolean matchingLC,
boolean onlyParseSimpleNames) {
Node typeNode = null;
if (onlyParseSimpleNames) {
typeNode = parse
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> {
return parseTypeExpression(token);
}
}
/**
* ParamTypeExpressionAnnotation :=
* '{' OptionalParameterType '}' |
* '{' TopLevelTypeExpression '}' |
* '{' '...' TopLevelTypeExpression '}'
*
* OptionalParameterType :=
* TopLevelTypeExpression '='
*/
private Node parseParamTypeExpressionAnnotation(JsDocToken token) {
Preconditions.checkArgument(token == JsDocToken.LC);
skipEOLs();
boolean restArg = false;
token = next();
if (token == JsDocToken.ELLIPSIS) {
token = next();
if (token == JsDocToken.RC) {
// EMPTY represents the UNKNOWN type in the Type AST.
return wrapNode(Token.ELLIPSIS, IR.empty());
}
restArg = true;
}
Node typeNode = parseTopLevelTypeExpression(token);
if (typeNode != null) {
skipEOLs();
if (restArg) {
typeNode = wrapNode(Token.ELLIPSIS, typeNode);
} else if (match(JsDocToken.EQUALS)) {
next();
skipEOLs();
typeNode = wrapNode(Token.EQUALS, typeNode);
}
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
}
/**
* TypeNameAnnotation := TypeName | '{' TypeName '}'
*/
private Node parseTypeNameAnnotation(JsDocToken token) {
if (token == JsDocToken.LC) {
skipEOLs();
Node typeNode = parseTypeName(next());
if (typeNode != null) {
skipEOLs();
if (!match(JsDocToken.RC)) {
reportTypeSyntaxWarning("msg.jsdoc.missing.rc");
} else {
next();
}
}
return typeNode;
} else {
return parseTypeName(token);
}
}
/**
* TopLevelTypeExpression := TypeExpression
* | TypeUnionList
*
* We made this rule up, for the sake of backwards compatibility.
*/
private Node parseTopLevelTypeExpression(JsDocToken token) {
Node
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> token) {
if (token != JsDocToken.STRING) {
return reportGenericTypeSyntaxWarning();
}
String typeName = stream.getString();
int lineno = stream.getLineno();
int charno = stream.getCharno();
while (match(JsDocToken.EOL) &&
typeName.charAt(typeName.length() - 1) == '.') {
skipEOLs();
if (match(JsDocToken.STRING)) {
next();
typeName += stream.getString();
}
}
Node typeNameNode = newStringNode(typeName, lineno, charno);
if (match(JsDocToken.LT)) {
next();
skipEOLs();
Node memberType = parseTypeExpressionList(next());
if (memberType != null) {
typeNameNode.addChildToFront(memberType);
skipEOLs();
if (!match(JsDocToken.GT)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.gt");
}
next();
}
}
return typeNameNode;
}
/**
* FunctionType := 'function' FunctionSignatureType
* FunctionSignatureType :=
* TypeParameters '(' 'this' ':' TypeName, ParametersType ')' ResultType
*/
private Node parseFunctionType(JsDocToken token) {
// NOTE(nicksantos): We're not implementing generics at the moment, so
// just throw out TypeParameters.
if (token != JsDocToken.LP) {
restoreLookAhead(token);
return reportTypeSyntaxWarning("msg.jsdoc.missing.lp");
}
Node functionType = newNode(Token.FUNCTION);
Node parameters = null;
skipEOLs();
if (!match(JsDocToken.RP)) {
token = next();
boolean hasParams = true;
if (token == JsDocToken.STRING) {
String tokenStr = stream.getString();
boolean isThis = "this".equals(tokenStr);
boolean isNew = "new".equals(tokenStr);
if (isThis || isNew) {
if (match(JsDocToken.COLON)) {
next();
skipEOLs();
Node contextType = wrapNode(
isThis ? Token.THIS : Token.NEW,
parseTypeName(next()));
if (contextType == null) {
return null;
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.VOID);
} else {
return parseTypeExpression(next());
}
}
/**
* UnionType := '(' TypeUnionList ')'
* TypeUnionList := TypeExpression | TypeExpression '|' TypeUnionList
*
* We've removed the empty union type.
*/
private Node parseUnionType(JsDocToken token) {
return parseUnionTypeWithAlternate(token, null);
}
/**
* Create a new union type, with an alternate that has already been
* parsed. The alternate may be null.
*/
private Node parseUnionTypeWithAlternate(JsDocToken token, Node alternate) {
Node union = newNode(Token.PIPE);
if (alternate != null) {
union.addChildToBack(alternate);
}
Node expr = null;
do {
if (expr != null) {
skipEOLs();
token = next();
Preconditions.checkState(
token == JsDocToken.PIPE || token == JsDocToken.COMMA);
boolean isPipe = token == JsDocToken.PIPE;
if (isPipe && match(JsDocToken.PIPE)) {
// We support double pipes for backwards compatibility.
next();
}
skipEOLs();
token = next();
}
expr = parseTypeExpression(token);
if (expr == null) {
return null;
}
union.addChildToBack(expr);
// We support commas for backwards compatibility.
} while (match(JsDocToken.PIPE, JsDocToken.COMMA));
if (alternate == null) {
skipEOLs();
if (!match(JsDocToken.RP)) {
return reportTypeSyntaxWarning("msg.jsdoc.missing.rp");
}
next();
}
return union;
}
/**
* ArrayType := '[' ElementTypeList ']'
* ElementTypeList := <empty> | TypeExpression | '...' TypeExpression
* | TypeExpression ',' ElementTypeList
*/
private Node parseArrayType(JsDocToken token) {
Node array = newNode(Token.LB);
Node arg = null;
boolean hasVarArgs = false;
do {
if (arg != null) {
next();
skipEOLs();
token = next();
}
if (token == JsDocToken.ELLIPSIS) {
arg = wrapNode(Token.ELLIPS
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>>true</code> if it exist.
*/
public final boolean hasNode(N n) {
return getNode(n) != null;
}
/**
* Checks whether two nodes in the graph are connected.
*
* @param n1 Node 1.
* @param n2 Node 2.
* @return <code>true</code> if the two nodes are connected.
*/
public abstract boolean isConnected(N n1, N n2);
/**
* Checks whether two nodes in the graph are connected by the given
* edge type.
*
* @param n1 Node 1.
* @param e The edge type.
* @param n2 Node 2.
*/
public abstract boolean isConnected(N n1, E e, N n2);
/**
* Gets the node of the specified type, or throws an
* IllegalArgumentException.
*/
@SuppressWarnings("unchecked")
<T extends GraphNode<N, E>> T getNodeOrFail(N val) {
T node = (T) getNode(val);
if (node == null) {
throw new IllegalArgumentException(val + " does not exist in graph");
}
return node;
}
@Override
public final void clearNodeAnnotations() {
for (GraphNode<N, E> n : getNodes()) {
n.setAnnotation(null);
}
}
/** Makes each edge's annotation null. */
public final void clearEdgeAnnotations() {
for (GraphEdge<N, E> e : getEdges()) {
e.setAnnotation(null);
}
}
/**
* Pushes nodes' annotation values. Restored with
* {@link #popNodeAnnotations()}. Nodes' annotation values are cleared.
*/
public final void pushNodeAnnotations() {
if (nodeAnnotationStack == null) {
nodeAnnotationStack = Lists.newLinkedList();
}
pushAnnotations(nodeAnnotationStack, getNodes());
}
/**
* Restores nodes' annotation values to state before last
* {@link #pushNodeAnnotations()}.
*/
public final void popNodeAnnotations() {
Preconditions.checkNotNull(nodeAnnotationStack,
"Popping node annotations without pushing.");
popAnnotations(nodeAnnotationStack);
}
/**
* Pushes edges' annotation values. Restored with
* {@link #popEdge
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Annotations()}. Edges' annotation values are cleared.
*/
public final void pushEdgeAnnotations() {
if (edgeAnnotationStack == null) {
edgeAnnotationStack = Lists.newLinkedList();
}
pushAnnotations(edgeAnnotationStack, getEdges());
}
/**
* Restores edges' annotation values to state before last
* {@link #pushEdgeAnnotations()}.
*/
public final void popEdgeAnnotations() {
Preconditions.checkNotNull(edgeAnnotationStack,
"Popping edge annotations without pushing.");
popAnnotations(edgeAnnotationStack);
}
/**
* A generic edge.
*
* @param <N> Value type that the graph node stores.
* @param <E> Value type that the graph edge stores.
*/
public interface GraphEdge<N, E> extends Annotatable {
/**
* Retrieves the edge's value.
*
* @return The value.
*/
E getValue();
GraphNode<N, E> getNodeA();
GraphNode<N, E> getNodeB();
}
/**
* A simple implementation of SubGraph that calculates adjacency by iterating
* over a node's neighbors.
*/
class SimpleSubGraph<N, E> implements SubGraph<N, E> {
private Graph<N, E> graph;
private List<GraphNode<N, E>> nodes = Lists.newArrayList();
SimpleSubGraph(Graph<N, E> graph) {
this.graph = graph;
}
@Override
public boolean isIndependentOf(N value) {
GraphNode<N, E> node = graph.getNode(value);
for (GraphNode<N, E> n : nodes) {
if (graph.getNeighborNodes(n.getValue()).contains(node)) {
return false;
}
}
return true;
}
@Override
public void addNode(N value) {
nodes.add(graph.getNodeOrFail(value));
}
}
/**
* Pushes a new list on stack and stores nodes annotations in the new list.
* Clears objects' annotations as well.
*/
private static void pushAnnotations(
Deque<GraphAnnotationState> stack,
Collection<? extends Annotatable> haveAnnotations) {
stack.push(new GraphAnnotationState(haveAnnotations.size()));
for (Annotatable h : haveAnnotations
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, name, isPropAssign, type);
} else {
handleSetFromLocal(t, n, parent, name);
}
} else {
handleGet(t, n, parent, name);
}
}
/**
* Gets the fully qualified name corresponding to an object literal key,
* as long as it and its prefix property names are valid JavaScript
* identifiers. The object literal may be nested inside of other object
* literals.
*
* For example, if called with node {@code n} representing "z" in any of
* the following expressions, the result would be "w.x.y.z":
* <code> var w = {x: {y: {z: 0}}}; </code>
* <code> w.x = {y: {z: 0}}; </code>
* <code> w.x.y = {'a': 0, 'z': 0}; </code>
*
* @param n A child of an OBJLIT node
* @return The global name, or null if {@code n} doesn't correspond to the
* key of an object literal that can be named
*/
String getNameForObjLitKey(Node n) {
Node parent = n.getParent();
Preconditions.checkState(parent.isObjectLit());
Node gramps = parent.getParent();
if (gramps == null) {
return null;
}
Node greatGramps = gramps.getParent();
String name;
switch (gramps.getType()) {
case Token.NAME:
// VAR
// NAME (gramps)
// OBJLIT (parent)
// STRING (n)
if (greatGramps == null || !greatGramps.isVar()) {
return null;
}
name = gramps.getString();
break;
case Token.ASSIGN:
// ASSIGN (gramps)
// NAME|GETPROP
// OBJLIT (parent)
// STRING (n)
Node lvalue = gramps.getFirstChild();
name = lvalue.getQualifiedName();
break;
case Token.STRING_KEY:
// OBJLIT
// STRING (gramps)
// OBJLIT (parent)
//
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> != null) {
for (Name n : props) {
if (!n.canCollapse()) {
return false;
}
}
}
return true;
}
boolean isSimpleStubDeclaration() {
if (getRefs().size() == 1) {
Ref ref = refs.get(0);
JSDocInfo info = ref.node.getJSDocInfo();
if (ref.node.getParent() != null &&
ref.node.getParent().isExprResult()) {
return true;
}
}
return false;
}
boolean canCollapse() {
return !inExterns && !isGetOrSetDefinition() && (declaredType ||
(parent == null || parent.canCollapseUnannotatedChildNames()) &&
(globalSets > 0 || localSets > 0) &&
deleteProps == 0);
}
boolean isGetOrSetDefinition() {
return this.type == Type.GET || this.type == Type.SET;
}
boolean canCollapseUnannotatedChildNames() {
if (type == Type.OTHER || isGetOrSetDefinition()
|| globalSets != 1 || localSets != 0 || deleteProps != 0) {
return false;
}
// Don't try to collapse if the one global set is a twin reference.
// We could theoretically handle this case in CollapseProperties, but
// it's probably not worth the effort.
Preconditions.checkNotNull(declaration);
if (declaration.getTwin() != null) {
return false;
}
if (declaredType) {
return true;
}
// If this is a key of an aliased object literal, then it will be aliased
// later. So we won't be able to collapse its properties.
if (parent != null && parent.shouldKeepKeys()) {
return false;
}
// If this is aliased, then its properties can't be collapsed either.
if (aliasingGets > 0) {
return false;
}
return (parent == null || parent.canCollapseUnannotatedChildNames());
}
/** Whether this is an object literal that needs to keep its keys. */
boolean shouldKeepKeys() {
return type == Type.OBJECTLIT && aliasingGets > 0;
}
boolean needsToBeStubbed() {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>() {
return source == null ? "" : source.getName();
}
Ref getTwin() {
return twin;
}
boolean isSet() {
return type == Type.SET_FROM_GLOBAL || type == Type.SET_FROM_LOCAL;
}
static void markTwins(Ref a, Ref b) {
Preconditions.checkArgument(
(a.type == Type.ALIASING_GET || b.type == Type.ALIASING_GET) &&
(a.type == Type.SET_FROM_GLOBAL || a.type == Type.SET_FROM_LOCAL ||
b.type == Type.SET_FROM_GLOBAL || b.type == Type.SET_FROM_LOCAL));
a.twin = b;
b.twin = a;
}
/**
* Create a new ref that is the same as this one, but of
* a different class.
*/
Ref cloneAndReclassify(Type type) {
return new Ref(this, type, this.preOrderIndex);
}
static Ref createRefForTesting(Type type) {
return new Ref(type, -1);
}
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this(ast, ast.getSourceFile().getName(), false);
}
public CompilerInput(SourceAst ast, boolean isExtern) {
this(ast, ast.getInputId(), isExtern);
}
public CompilerInput(SourceAst ast, String inputId, boolean isExtern) {
this(ast, new InputId(inputId), isExtern);
}
public CompilerInput(SourceAst ast, InputId inputId, boolean isExtern) {
this.ast = ast;
this.id = inputId;
// TODO(nicksantos): Add a precondition check here. People are passing
// in null, but they should not be.
if (ast != null && ast.getSourceFile() != null) {
ast.getSourceFile().setIsExtern(isExtern);
}
}
public CompilerInput(SourceFile file) {
this(file, false);
}
public CompilerInput(SourceFile file, boolean isExtern) {
this(new JsAst(file), isExtern);
}
/** Returns a name for this input. Must be unique across all inputs. */
@Override
public InputId getInputId() {
return id;
}
/** Returns a name for this input. Must be unique across all inputs. */
@Override
public String getName() {
return id.getIdName();
}
public SourceAst getAst() {
return ast;
}
/** Gets the path relative to closure-base, if one is available. */
@Override
public String getPathRelativeToClosureBase() {
// TODO(nicksantos): Implement me.
throw new UnsupportedOperationException();
}
@Override
public Node getAstRoot(AbstractCompiler compiler) {
Node root = ast.getAstRoot(compiler);
// The root maybe null if the AST can not be created.
if (root != null) {
Preconditions.checkState(root.isScript());
Preconditions.checkNotNull(root.getInputId());
}
return root;
}
@Override
public void clearAst() {
ast.clearAst();
}
@Override
public SourceFile getSourceFile() {
return ast.getSourceFile();
}
@Override
public void setSourceFile(SourceFile file) {
ast.setSourceFile(file);
}
/** Returns the SourceAst object on
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> which this input is based. */
public SourceAst getSourceAst() {
return ast;
}
/** Sets an abstract compiler for doing parsing. */
public void setCompiler(AbstractCompiler compiler) {
this.compiler = compiler;
}
private void checkErrorManager() {
Preconditions.checkNotNull(compiler,
"Expected setCompiler to be called first: " + this);
Preconditions.checkNotNull(compiler.getErrorManager(),
"Expected compiler to call an error manager: " + this);
}
/** Gets a list of types depended on by this input. */
@Override
public Collection<String> getRequires() {
checkErrorManager();
try {
regenerateDependencyInfoIfNecessary();
return Collections.<String>unmodifiableSet(requires);
} catch (IOException e) {
compiler.getErrorManager().report(CheckLevel.ERROR,
JSError.make(AbstractCompiler.READ_ERROR, getName()));
return ImmutableList.<String>of();
}
}
/** Gets a list of types provided by this input. */
@Override
public Collection<String> getProvides() {
checkErrorManager();
try {
regenerateDependencyInfoIfNecessary();
return Collections.<String>unmodifiableSet(provides);
} catch (IOException e) {
compiler.getErrorManager().report(CheckLevel.ERROR,
JSError.make(AbstractCompiler.READ_ERROR, getName()));
return ImmutableList.<String>of();
}
}
// TODO(nicksantos): Remove addProvide/addRequire/removeRequire once
// there is better support for discovering non-closure dependencies.
void addProvide(String provide) {
getProvides();
provides.add(provide);
}
void addRequire(String require) {
getRequires();
requires.add(require);
}
public void removeRequire(String require) {
getRequires();
requires.remove(require);
}
/**
* Regenerates the provides/requires if we need to do so.
*/
private void regenerateDependencyInfoIfNecessary() throws IOException {
// If the code is NOT a JsAst, then it was not originally JS code.
// Look at the Ast for dependency info.
if (!(ast instanceof JsAst)) {
Preconditions.checkNotNull(compiler,
"Expected setCompiler to be called first");
DepsFinder finder =
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Result() &&
!parent.isScript()) {
return;
}
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
visitSubtree(child, n);
}
}
}
/**
* Gets the source line for the indicated line number.
*
* @param lineNumber the line number, 1 being the first line of the file.
* @return The line indicated. Does not include the newline at the end
* of the file. Returns {@code null} if it does not exist,
* or if there was an IO exception.
*/
public String getLine(int lineNumber) {
return getSourceFile().getLine(lineNumber);
}
/**
* Get a region around the indicated line number. The exact definition of a
* region is implementation specific, but it must contain the line indicated
* by the line number. A region must not start or end by a carriage return.
*
* @param lineNumber the line number, 1 being the first line of the file.
* @return The line indicated. Returns {@code null} if it does not exist,
* or if there was an IO exception.
*/
public Region getRegion(int lineNumber) {
return getSourceFile().getRegion(lineNumber);
}
public String getCode() throws IOException {
return getSourceFile().getCode();
}
/** Returns the module to which the input belongs. */
public JSModule getModule() {
return module;
}
/** Sets the module to which the input belongs. */
public void setModule(JSModule module) {
// An input may only belong to one module.
Preconditions.checkArgument(
module == null || this.module == null || this.module == module);
this.module = module;
}
/** Overrides the module to which the input belongs. */
void overrideModule(JSModule module) {
this.module = module;
}
public boolean isExtern() {
if (ast == null || ast.getSourceFile() == null) {
return false;
}
return ast.getSourceFile().isExtern();
}
void setIsExtern(boolean isExtern) {
if (ast == null || ast.getSourceFile() == null) {
return;
}
ast.getSourceFile().
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.google.javascript.rhino.jstype.JSType;
import com.google.javascript.rhino.jstype.JSTypeNative;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import com.google.javascript.rhino.jstype.ObjectType;
import com.google.javascript.rhino.jstype.ParameterizedType;
import com.google.javascript.rhino.jstype.StaticSlot;
import com.google.javascript.rhino.jstype.TemplateType;
import com.google.javascript.rhino.jstype.UnionType;
import com.google.javascript.rhino.jstype.Visitor;
/**
* Chainable reverse abstract interpreter providing basic functionality.
*
*/
public abstract class ChainableReverseAbstractInterpreter
implements ReverseAbstractInterpreter {
protected final CodingConvention convention;
final JSTypeRegistry typeRegistry;
private ChainableReverseAbstractInterpreter firstLink;
private ChainableReverseAbstractInterpreter nextLink;
/**
* Constructs an interpreter, which is the only link in a chain. Interpreters
* can be appended using {@link #append}.
*/
public ChainableReverseAbstractInterpreter(CodingConvention convention,
JSTypeRegistry typeRegistry) {
Preconditions.checkNotNull(convention);
this.convention = convention;
this.typeRegistry = typeRegistry;
firstLink = this;
nextLink = null;
}
/**
* Appends a link to {@code this}, returning the updated last link.
* <p>
* The pattern {@code new X().append(new Y())...append(new Z())} forms a
* chain starting with X, then Y, then ... Z.
* @param lastLink a chainable interpreter, with no next link
* @return the updated last link
*/
public ChainableReverseAbstractInterpreter append(
ChainableReverseAbstractInterpreter lastLink) {
Preconditions.checkArgument(lastLink.nextLink == null);
this.nextLink = lastLink;
lastLink.firstLink = this.firstLink;
return lastLink;
}
/**
* Gets the first link of this chain.
*/
public ChainableReverseAbstractInterpreter getFirst() {
return firstLink;
}
/**
* Calculates the preciser scope starting with the first link.
*/
protected FlowScope firstPrec
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>NameInScope(FlowScope scope, Node node, JSType type) {
switch (node.getType()) {
case Token.NAME:
scope.inferSlotType(node.getString(), type);
break;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
Preconditions.checkNotNull(qualifiedName);
JSType origType = node.getJSType();
origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType;
scope.inferQualifiedSlot(node, qualifiedName, origType, type);
break;
case Token.THIS:
// "this" references aren't currently modeled in the CFG.
break;
default:
throw new IllegalArgumentException("Node cannot be refined. \n" +
node.toStringTree());
}
}
/**
* @see #getRestrictedWithoutUndefined(JSType)
*/
private final Visitor<JSType> restrictUndefinedVisitor =
new Visitor<JSType>() {
@Override
public JSType caseEnumElementType(EnumElementType enumElementType) {
JSType type = enumElementType.getPrimitiveType().visit(this);
if (type != null && enumElementType.getPrimitiveType().equals(type)) {
return enumElementType;
} else {
return type;
}
}
@Override
public JSType caseAllType() {
return typeRegistry.createUnionType(OBJECT_TYPE, NUMBER_TYPE,
STRING_TYPE, BOOLEAN_TYPE, NULL_TYPE);
}
@Override
public JSType caseNoObjectType() {
return getNativeType(NO_OBJECT_TYPE);
}
@Override
public JSType caseNoType() {
return getNativeType(NO_TYPE);
}
@Override
public JSType caseBooleanType() {
return getNativeType(BOOLEAN_TYPE);
}
@Override
public JSType caseFunctionType(FunctionType type) {
return type;
}
@Override
public JSType caseNullType() {
return getNativeType(NULL_TYPE);
}
@Override
public JSType caseNumberType() {
return getNativeType(NUMBER_TYPE);
}
@Override
public JSType caseObjectType(ObjectType type) {
return type;
}
@Override
public JSType caseStringType() {
return getNativeType(STRING_TYPE);
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> case '\uFEFF': // <BOM>
return TernaryValue.TRUE;
default:
return (Character.getType(c) == Character.SPACE_SEPARATOR)
? TernaryValue.TRUE : TernaryValue.FALSE;
}
}
/**
* Gets the function's name. This method recognizes five forms:
* <ul>
* <li>{@code function name() ...}</li>
* <li>{@code var name = function() ...}</li>
* <li>{@code qualified.name = function() ...}</li>
* <li>{@code var name2 = function name1() ...}</li>
* <li>{@code qualified.name2 = function name1() ...}</li>
* </ul>
* In two last cases with named function expressions, the second name is
* returned (the variable of qualified name).
*
* @param n a node whose type is {@link Token#FUNCTION}
* @return the function's name, or {@code null} if it has no name
*/
static String getFunctionName(Node n) {
Preconditions.checkState(n.isFunction());
Node parent = n.getParent();
switch (parent.getType()) {
case Token.NAME:
// var name = function() ...
// var name2 = function name1() ...
return parent.getQualifiedName();
case Token.ASSIGN:
// qualified.name = function() ...
// qualified.name2 = function name1() ...
return parent.getFirstChild().getQualifiedName();
default:
// function name() ...
String name = n.getFirstChild().getQualifiedName();
return name;
}
}
/**
* Gets the function's name. This method recognizes the forms:
* <ul>
* <li>{@code {'name': function() ...}}</li>
* <li>{@code {name: function() ...}}</li>
* <li>{@code function name() ...}</li>
* <li>{@code var name = function() ...}</li>
* <li>{@code qualified.name = function() ...}</li>
* <li>{@code var name2 = function name1() ...}</li>
* <li>{@code qualified.name2 = function name1()
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Child().isString()
&& STRING_REGEXP_METHODS.contains(
nameNode.getLastChild().getString())) {
Node param = nameNode.getNext();
if (param != null &&
(param.isString() || param.isRegExp()))
return false;
}
}
}
return true;
}
/**
* @return Whether the call has a local result.
*/
static boolean callHasLocalResult(Node n) {
Preconditions.checkState(n.isCall());
return (n.getSideEffectFlags() & Node.FLAG_LOCAL_RESULTS) > 0;
}
/**
* @return Whether the new has a local result.
*/
static boolean newHasLocalResult(Node n) {
Preconditions.checkState(n.isNew());
return n.isOnlyModifiesThisCall();
}
/**
* Returns true if the current node's type implies side effects.
*
* This is a non-recursive version of the may have side effects
* check; used to check wherever the current node's type is one of
* the reason's why a subtree has side effects.
*/
static boolean nodeTypeMayHaveSideEffects(Node n) {
return nodeTypeMayHaveSideEffects(n, null);
}
static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) {
if (isAssignmentOp(n)) {
return true;
}
switch(n.getType()) {
case Token.DELPROP:
case Token.DEC:
case Token.INC:
case Token.THROW:
return true;
case Token.CALL:
return NodeUtil.functionCallHasSideEffects(n, compiler);
case Token.NEW:
return NodeUtil.constructorCallHasSideEffects(n, compiler);
case Token.NAME:
// A variable definition.
return n.hasChildren();
default:
return false;
}
}
/**
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n) {
Set<String> emptySet = Collections.emptySet();
return canBeSideEffected(n, emptySet);
}
/**
* @param knownConstants A set of names known to be constant value at
* node
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> 'n' (such as locals that are last written before n can execute).
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n, Set<String> knownConstants) {
switch (n.getType()) {
case Token.CALL:
case Token.NEW:
// Function calls or constructor can reference changed values.
// TODO(johnlenz): Add some mechanism for determining that functions
// are unaffected by side effects.
return true;
case Token.NAME:
// Non-constant names values may have been changed.
return !isConstantName(n)
&& !knownConstants.contains(n.getString());
// Properties on constant NAMEs can still be side-effected.
case Token.GETPROP:
case Token.GETELEM:
return true;
case Token.FUNCTION:
// Function expression are not changed by side-effects,
// and function declarations are not part of expressions.
Preconditions.checkState(isFunctionExpression(n));
return false;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (canBeSideEffected(c, knownConstants)) {
return true;
}
}
return false;
}
/*
* 0 comma ,
* 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |=
* 2 conditional ?:
* 3 logical-or ||
* 4 logical-and &&
* 5 bitwise-or |
* 6 bitwise-xor ^
* 7 bitwise-and &
* 8 equality == !=
* 9 relational < <= > >=
* 10 bitwise shift << >> >>>
* 11 addition/subtraction + -
* 12 multiply/divide * / %
* 13 negation/increment ! ~ - ++ --
* 14 call, member () [] .
*/
static int precedence(int type) {
switch (type) {
case Token.COMMA: return 0;
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
return Token.LSH;
case Token.ASSIGN_RSH:
return Token.RSH;
case Token.ASSIGN_URSH:
return Token.URSH;
case Token.ASSIGN_ADD:
return Token.ADD;
case Token.ASSIGN_SUB:
return Token.SUB;
case Token.ASSIGN_MUL:
return Token.MUL;
case Token.ASSIGN_DIV:
return Token.DIV;
case Token.ASSIGN_MOD:
return Token.MOD;
}
throw new IllegalArgumentException("Not an assignment op:" + n);
}
/**
* Determines if the given node contains a function statement or function
* expression.
*/
static boolean containsFunction(Node n) {
return containsType(n, Token.FUNCTION);
}
/**
* Returns true if the shallow scope contains references to 'this' keyword
*/
static boolean referencesThis(Node n) {
Node start = (n.isFunction()) ? n.getLastChild() : n;
return containsType(start, Token.THIS, MATCH_NOT_FUNCTION);
}
/**
* Is this a GETPROP or GETELEM node?
*/
static boolean isGet(Node n) {
return n.isGetProp() || n.isGetElem();
}
/**
* Is this node the name of a variable being declared?
*
* @param n The node
* @return True if {@code n} is NAME and {@code parent} is VAR
*/
static boolean isVarDeclaration(Node n) {
// There is no need to verify that parent != null because a NAME node
// always has a parent in a valid parse tree.
return n.isName() && n.getParent().isVar();
}
/**
* For an assignment or variable declaration get the assigned value.
* @return The value node representing the new value.
*/
static Node getAssignedValue(Node n) {
Preconditions.checkState(n.isName());
Node parent = n.getParent();
if (parent.isVar()) {
return n.getFirstChild();
} else if (parent.isAssign() && parent.getFirstChild() == n) {
return n.getNext();
} else {
return null;
}
}
/**
* Is this node an assignment expression statement?
*
*
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
case Token.WITH:
case Token.IF:
case Token.LABEL:
case Token.TRY:
case Token.CATCH:
case Token.SWITCH:
case Token.CASE:
case Token.DEFAULT_CASE:
return true;
default:
return false;
}
}
/**
* Determines whether the given node is code node for FOR, DO,
* WHILE, WITH, or IF node.
*/
static boolean isControlStructureCodeBlock(Node parent, Node n) {
switch (parent.getType()) {
case Token.FOR:
case Token.WHILE:
case Token.LABEL:
case Token.WITH:
return parent.getLastChild() == n;
case Token.DO:
return parent.getFirstChild() == n;
case Token.IF:
return parent.getFirstChild() != n;
case Token.TRY:
return parent.getFirstChild() == n || parent.getLastChild() == n;
case Token.CATCH:
return parent.getLastChild() == n;
case Token.SWITCH:
case Token.CASE:
return parent.getFirstChild() != n;
case Token.DEFAULT_CASE:
return true;
default:
Preconditions.checkState(isControlStructure(parent));
return false;
}
}
/**
* Gets the condition of an ON_TRUE / ON_FALSE CFG edge.
* @param n a node with an outgoing conditional CFG edge
* @return the condition node or null if the condition is not obviously a node
*/
static Node getConditionExpression(Node n) {
switch (n.getType()) {
case Token.IF:
case Token.WHILE:
return n.getFirstChild();
case Token.DO:
return n.getLastChild();
case Token.FOR:
switch (n.getChildCount()) {
case 3:
return null;
case 4:
return n.getFirstChild().getNext();
}
throw new IllegalArgumentException("malformed 'for' statement " + n);
case Token.CASE:
return null;
}
throw new IllegalArgumentException(n + " does not have a condition.");
}
/**
* @return Whether the node is of a type
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> that contain other statements.
*/
static boolean isStatementBlock(Node n) {
return n.isScript() || n.isBlock();
}
/**
* @return Whether the node is used as a statement.
*/
static boolean isStatement(Node n) {
return isStatementParent(n.getParent());
}
static boolean isStatementParent(Node parent) {
// It is not possible to determine definitely if a node is a statement
// or not if it is not part of the AST. A FUNCTION node can be
// either part of an expression or a statement.
Preconditions.checkState(parent != null);
switch (parent.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.LABEL:
return true;
default:
return false;
}
}
/** Whether the node is part of a switch statement. */
static boolean isSwitchCase(Node n) {
return n.isCase() || n.isDefaultCase();
}
/**
* @return Whether the name is a reference to a variable, function or
* function parameter (not a label or a empty function expression name).
*/
static boolean isReferenceName(Node n) {
return n.isName() && !n.getString().isEmpty();
}
/** Whether the child node is the FINALLY block of a try. */
static boolean isTryFinallyNode(Node parent, Node child) {
return parent.isTry() && parent.getChildCount() == 3
&& child == parent.getLastChild();
}
/** Whether the node is a CATCH container BLOCK. */
static boolean isTryCatchNodeContainer(Node n) {
Node parent = n.getParent();
return parent.isTry()
&& parent.getFirstChild().getNext() == n;
}
/** Safely remove children while maintaining a valid node structure. */
static void removeChild(Node parent, Node node) {
if (isTryFinallyNode(parent, node)) {
if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) {
// A finally can only be removed if there is a catch.
parent.removeChild(node);
} else {
// Otherwise, only its children can be removed.
node.detachChildren();
}
} else if (node.isCatch()) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> // The CATCH can can only be removed if there is a finally clause.
Node tryNode = node.getParent().getParent();
Preconditions.checkState(NodeUtil.hasFinally(tryNode));
node.detachFromParent();
} else if (isTryCatchNodeContainer(node)) {
// The container node itself can't be removed, but the contained CATCH
// can if there is a 'finally' clause
Node tryNode = node.getParent();
Preconditions.checkState(NodeUtil.hasFinally(tryNode));
node.detachChildren();
} else if (node.isBlock()) {
// Simply empty the block. This maintains source location and
// "synthetic"-ness.
node.detachChildren();
} else if (isStatementBlock(parent)
|| isSwitchCase(node)) {
// A statement in a block can simply be removed.
parent.removeChild(node);
} else if (parent.isVar()) {
if (parent.hasMoreThanOneChild()) {
parent.removeChild(node);
} else {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// This would leave an empty VAR, remove the VAR itself.
removeChild(parent.getParent(), parent);
}
} else if (parent.isLabel()
&& node == parent.getLastChild()) {
// Remove the node from the parent, so it can be reused.
parent.removeChild(node);
// A LABEL without children can not be referred to, remove it.
removeChild(parent.getParent(), parent);
} else if (parent.isFor()
&& parent.getChildCount() == 4) {
// Only Token.FOR can have an Token.EMPTY other control structure
// need something for the condition. Others need to be replaced
// or the structure removed.
parent.replaceChild(node, IR.empty());
} else {
throw new IllegalStateException("Invalid attempt to remove node: " +
node.toString() + " of "+ parent.toString());
}
}
/**
* Add a finally block if one does not exist.
*/
static void maybeAddFinally(Node tryNode) {
Preconditions.checkState(tryNode.isTry());
if (!NodeUtil.hasFinally(tryNode)) {
tryNode.addChildrenToBack(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>IR.block().srcref(tryNode));
}
}
/**
* Merge a block with its parent block.
* @return Whether the block was removed.
*/
static boolean tryMergeBlock(Node block) {
Preconditions.checkState(block.isBlock());
Node parent = block.getParent();
// Try to remove the block if its parent is a block/script or if its
// parent is label and it has exactly one child.
if (isStatementBlock(parent)) {
Node previous = block;
while (block.hasChildren()) {
Node child = block.removeFirstChild();
parent.addChildAfter(child, previous);
previous = child;
}
parent.removeChild(block);
return true;
} else {
return false;
}
}
/**
* @param node A node
* @return Whether the call is a NEW or CALL node.
*/
static boolean isCallOrNew(Node node) {
return node.isCall() || node.isNew();
}
/**
* Return a BLOCK node for the given FUNCTION node.
*/
static Node getFunctionBody(Node fn) {
Preconditions.checkArgument(fn.isFunction());
return fn.getLastChild();
}
/**
* Is this node or any of its children a CALL?
*/
static boolean containsCall(Node n) {
return containsType(n, Token.CALL);
}
/**
* Is this node a function declaration? A function declaration is a function
* that has a name that is added to the current scope (i.e. a function that
* is not part of a expression; see {@link #isFunctionExpression}).
*/
static boolean isFunctionDeclaration(Node n) {
return n.isFunction() && isStatement(n);
}
/**
* Is this node a hoisted function declaration? A function declaration in the
* scope root is hoisted to the top of the scope.
* See {@link #isFunctionDeclaration}).
*/
static boolean isHoistedFunctionDeclaration(Node n) {
return isFunctionDeclaration(n)
&& (n.getParent().isScript()
|| n.getParent().getParent().isFunction());
}
/**
* Is a FUNCTION node an function expression? An function expression is one
* that has either no name or a name that is not added to
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> the current scope.
*
* <p>Some examples of function expressions:
* <pre>
* (function () {})
* (function f() {})()
* [ function f() {} ]
* var f = function f() {};
* for (function f() {};;) {}
* </pre>
*
* <p>Some examples of functions that are <em>not</em> expressions:
* <pre>
* function f() {}
* if (x); else function f() {}
* for (;;) { function f() {} }
* </pre>
*
* @param n A node
* @return Whether n is an function used within an expression.
*/
static boolean isFunctionExpression(Node n) {
return n.isFunction() && !isStatement(n);
}
/**
* Determines if a node is a function expression that has an empty body.
*
* @param node a node
* @return whether the given node is a function expression that is empty
*/
static boolean isEmptyFunctionExpression(Node node) {
return isFunctionExpression(node) && isEmptyBlock(node.getLastChild());
}
/**
* Determines if a function takes a variable number of arguments by
* looking for references to the "arguments" var_args object.
*/
static boolean isVarArgsFunction(Node function) {
// TODO(johnlenz): rename this function
Preconditions.checkArgument(function.isFunction());
return isNameReferenced(
function.getLastChild(),
"arguments",
MATCH_NOT_FUNCTION);
}
/**
* @return Whether node is a call to methodName.
* a.f(...)
* a['f'](...)
*/
static boolean isObjectCallMethod(Node callNode, String methodName) {
if (callNode.isCall()) {
Node functionIndentifyingExpression = callNode.getFirstChild();
if (isGet(functionIndentifyingExpression)) {
Node last = functionIndentifyingExpression.getLastChild();
if (last != null && last.isString()) {
String propName = last.getString();
return (propName.equals(methodName));
}
}
}
return false;
}
/**
* @return Whether the callNode represents an expression in the form of:
* x.call(...)
* x['
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> we only do this
* because it makes sense to treat this as syntactically similar to
* "var x = 0;".
*
* @param n The node
* @return True if n is an L-value.
*/
static boolean isLValue(Node n) {
Preconditions.checkArgument(n.isName() || n.isGetProp() ||
n.isGetElem());
Node parent = n.getParent();
return (NodeUtil.isAssignmentOp(parent) && parent.getFirstChild() == n)
|| (NodeUtil.isForIn(parent) && parent.getFirstChild() == n)
|| parent.isVar()
|| (parent.isFunction() && parent.getFirstChild() == n)
|| parent.isDec()
|| parent.isInc()
|| parent.isParamList()
|| parent.isCatch();
}
/**
* Determines whether a node represents an object literal key
* (e.g. key1 in {key1: value1, key2: value2}).
*
* @param node A node
* @param parent The node's parent
*/
static boolean isObjectLitKey(Node node, Node parent) {
switch (node.getType()) {
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return true;
}
return false;
}
/**
* Get the name of an object literal key.
*
* @param key A node
*/
static String getObjectLitKeyName(Node key) {
switch (key.getType()) {
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return key.getString();
}
throw new IllegalStateException("Unexpected node type: " + key);
}
/**
* @param key A OBJECTLIT key node.
* @return The type expected when using the key.
*/
static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) {
if (valueType != null) {
switch (key.getType()) {
case Token.GETTER_DEF:
// GET must always return a function type.
if (valueType.isFunctionType()) {
FunctionType fntype = valueType.toMaybeFunctionType();
value
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = IR.var(
IR.name(nameNode.getString())
.srcref(nameNode))
.srcref(nameNode);
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
addingRoot = ancestor.getLastChild();
break;
}
}
// make sure that the adding root looks ok
Preconditions.checkState(addingRoot.isBlock() ||
addingRoot.isScript());
Preconditions.checkState(addingRoot.getFirstChild() == null ||
!addingRoot.getFirstChild().isScript());
return addingRoot;
}
/**
* Creates a node representing a qualified name.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @return A NAME or GETPROP node
*/
public static Node newQualifiedNameNode(
CodingConvention convention, String name) {
int endPos = name.indexOf('.');
if (endPos == -1) {
return newName(convention, name);
}
Node node = newName(convention, name.substring
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(0, endPos));
int startPos;
do {
startPos = endPos + 1;
endPos = name.indexOf('.', startPos);
String part = (endPos == -1
? name.substring(startPos)
: name.substring(startPos, endPos));
Node propNode = IR.string(part);
if (convention.isConstantKey(part)) {
propNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
node = IR.getprop(node, propNode);
} while (endPos != -1);
return node;
}
/**
* Creates a node representing a qualified name, copying over the source
* location information from the basis node and assigning the given original
* name to the node.
*
* @param name A qualified name (e.g. "foo" or "foo.bar.baz")
* @param basisNode The node that represents the name as currently found in
* the AST.
* @param originalName The original name of the item being represented by the
* NAME node. Used for debugging information.
*
* @return A NAME or GETPROP node
*/
static Node newQualifiedNameNode(
CodingConvention convention, String name, Node basisNode,
String originalName) {
Node node = newQualifiedNameNode(convention, name);
setDebugInformation(node, basisNode, originalName);
return node;
}
/**
* Gets the root node of a qualified name. Must be either NAME or THIS.
*/
public static Node getRootOfQualifiedName(Node qName) {
for (Node current = qName; true;
current = current.getFirstChild()) {
if (current.isName() || current.isThis()) {
return current;
}
Preconditions.checkState(current.isGetProp());
}
}
/**
* Sets the debug information (source file info and original name)
* on the given node.
*
* @param node The node on which to set the debug information.
* @param basisNode The basis node from which to copy the source file info.
* @param originalName The original name of the node.
*/
static void setDebugInformation(Node node, Node basisNode,
String originalName) {
node.copy
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> *
* @return True if all characters in the string are in Basic Latin set.
*/
static boolean isLatin(String s) {
char LARGEST_BASIC_LATIN = 0x7f;
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c > LARGEST_BASIC_LATIN) {
return false;
}
}
return true;
}
/**
* Determines whether the given name is a valid variable name.
*/
public static boolean isValidSimpleName(String name) {
return TokenStream.isJSIdentifier(name) &&
!TokenStream.isKeyword(name) &&
// no Unicode escaped characters - some browsers are less tolerant
// of Unicode characters that might be valid according to the
// language spec.
// Note that by this point, Unicode escapes have been converted
// to UTF-16 characters, so we're only searching for character
// values, not escapes.
isLatin(name);
}
/**
* Determines whether the given name is a valid qualified name.
*/
// TODO(nicksantos): This should be moved into a "Language" API,
// so that the results are different for es5 and es3.
public static boolean isValidQualifiedName(String name) {
if (name.endsWith(".") || name.startsWith(".")) {
return false;
}
String[] parts = name.split("\\.");
for (String part : parts) {
if (!isValidSimpleName(part)) {
return false;
}
}
return true;
}
/**
* Determines whether the given name can appear on the right side of
* the dot operator. Many properties (like reserved words) cannot.
*/
static boolean isValidPropertyName(String name) {
return isValidSimpleName(name);
}
private static class VarCollector implements Visitor {
final Map<String, Node> vars = Maps.newLinkedHashMap();
@Override
public void visit(Node n) {
if (n.isName()) {
Node parent = n.getParent();
if (parent != null && parent.isVar()) {
String name = n.getString();
if (!vars.containsKey(name)) {
vars.put(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> {
Node nodeName = IR.name(name);
if (value != null) {
Preconditions.checkState(value.getNext() == null);
nodeName.addChildToBack(value);
nodeName.srcref(value);
}
Node var = IR.var(nodeName).srcref(nodeName);
return var;
}
/**
* A predicate for matching name nodes with the specified node.
*/
private static class MatchNameNode implements Predicate<Node>{
final String name;
MatchNameNode(String name){
this.name = name;
}
@Override
public boolean apply(Node n) {
return n.isName() && n.getString().equals(name);
}
}
/**
* A predicate for matching nodes with the specified type.
*/
static class MatchNodeType implements Predicate<Node>{
final int type;
MatchNodeType(int type){
this.type = type;
}
@Override
public boolean apply(Node n) {
return n.getType() == type;
}
}
/**
* A predicate for matching var or function declarations.
*/
static class MatchDeclaration implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return isFunctionDeclaration(n) || n.isVar();
}
}
/**
* A predicate for matching anything except function nodes.
*/
private static class MatchNotFunction implements Predicate<Node>{
@Override
public boolean apply(Node n) {
return !n.isFunction();
}
}
static final Predicate<Node> MATCH_NOT_FUNCTION = new MatchNotFunction();
/**
* A predicate for matching statements without exiting the current scope.
*/
static class MatchShallowStatement implements Predicate<Node>{
@Override
public boolean apply(Node n) {
Node parent = n.getParent();
return n.isBlock()
|| (!n.isFunction() && (parent == null
|| isControlStructure(parent)
|| isStatementBlock(parent)));
}
}
/**
* Finds the number of times a type is referenced within the node tree.
*/
static int getNodeTypeReferenceCount(
Node node, int type, Predicate<Node> traverseChildrenPred) {
return getCount(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* Whether a simple
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>) {
visitor.visit(node);
if (traverseChildrenPred.apply(node)) {
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
visitPreOrder(c, visitor, traverseChildrenPred);
}
}
}
/**
* A post-order traversal, calling Visitor.visit for each child matching
* the predicate.
*/
static void visitPostOrder(Node node,
Visitor visitor,
Predicate<Node> traverseChildrenPred) {
if (traverseChildrenPred.apply(node)) {
for (Node c = node.getFirstChild(); c != null; c = c.getNext()) {
visitPostOrder(c, visitor, traverseChildrenPred);
}
}
visitor.visit(node);
}
/**
* @return Whether a TRY node has a finally block.
*/
static boolean hasFinally(Node n) {
Preconditions.checkArgument(n.isTry());
return n.getChildCount() == 3;
}
/**
* @return The BLOCK node containing the CATCH node (if any)
* of a TRY.
*/
static Node getCatchBlock(Node n) {
Preconditions.checkArgument(n.isTry());
return n.getFirstChild().getNext();
}
/**
* @return Whether BLOCK (from a TRY node) contains a CATCH.
* @see NodeUtil#getCatchBlock
*/
static boolean hasCatchHandler(Node n) {
Preconditions.checkArgument(n.isBlock());
return n.hasChildren() && n.getFirstChild().isCatch();
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
public static Node getFunctionParameters(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.isFunction());
return fnNode.getFirstChild().getNext();
}
/**
* Returns true if a name node represents a constant variable.
*
* <p>Determining whether a variable is constant has three steps:
* <ol>
* <li>In CodingConventionAnnotator, any name that matches the
* {@link CodingConvention#isConstant(String)} is annotated with an
* IS_
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>CONSTANT_NAME property.
* <li>The normalize pass renames any variable with the IS_CONSTANT_NAME
* annotation and that is initialized to a constant value with
* a variable name including $$constant.
* <li>Return true here if the variable includes $$constant in its name.
* </ol>
*
* @param node A NAME or STRING node
* @return True if the variable is constant
*/
static boolean isConstantName(Node node) {
return node.getBooleanProp(Node.IS_CONSTANT_NAME);
}
/** Whether the given name is constant by coding convention. */
static boolean isConstantByConvention(
CodingConvention convention, Node node, Node parent) {
String name = node.getString();
if (parent.isGetProp() &&
node == parent.getLastChild()) {
return convention.isConstantKey(name);
} else if (isObjectLitKey(node, parent)) {
return convention.isConstantKey(name);
} else {
return convention.isConstant(name);
}
}
/**
* Get the JSDocInfo for a function.
*/
public static JSDocInfo getFunctionJSDocInfo(Node n) {
Preconditions.checkState(n.isFunction());
JSDocInfo fnInfo = n.getJSDocInfo();
if (fnInfo == null && NodeUtil.isFunctionExpression(n)) {
// Look for the info on other nodes.
Node parent = n.getParent();
if (parent.isAssign()) {
// on ASSIGNs
fnInfo = parent.getJSDocInfo();
} else if (parent.isName()) {
// on var NAME = function() { ... };
fnInfo = parent.getParent().getJSDocInfo();
}
}
return fnInfo;
}
/**
* @param n The node.
* @return The source name property on the node or its ancestors.
*/
public static String getSourceName(Node n) {
String sourceName = null;
while (sourceName == null && n != null) {
sourceName = n.getSourceFileName();
n = n.getParent();
}
return sourceName;
}
/**
* @param n The node.
* @return The source name property on the node or
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Given the function, this returns the nth
* argument or null if no such parameter exists.
*/
static Node getArgumentForFunction(Node function, int index) {
Preconditions.checkState(function.isFunction());
return getNthSibling(
function.getFirstChild().getNext().getFirstChild(), index);
}
/**
* Given the new or call, this returns the nth
* argument of the call or null if no such argument exists.
*/
static Node getArgumentForCallOrNew(Node call, int index) {
Preconditions.checkState(isCallOrNew(call));
return getNthSibling(
call.getFirstChild().getNext(), index);
}
private static boolean isToStringMethodCall(Node call) {
Node getNode = call.getFirstChild();
if (isGet(getNode)) {
Node propNode = getNode.getLastChild();
return propNode.isString() && "toString".equals(propNode.getString());
}
return false;
}
/** Find the best JSDoc for the given node. */
static JSDocInfo getBestJSDocInfo(Node n) {
JSDocInfo info = n.getJSDocInfo();
if (info == null) {
Node parent = n.getParent();
if (parent == null) {
return null;
}
if (parent.isName()) {
return getBestJSDocInfo(parent);
} else if (parent.isAssign()) {
return parent.getJSDocInfo();
} else if (isObjectLitKey(parent, parent.getParent())) {
return parent.getJSDocInfo();
} else if (parent.isFunction()) {
return parent.getJSDocInfo();
} else if (parent.isVar() && parent.hasOneChild()) {
return parent.getJSDocInfo();
} else if ((parent.isHook() && parent.getFirstChild() != n) ||
parent.isOr() ||
parent.isAnd() ||
(parent.isComma() && parent.getFirstChild() != n)) {
return getBestJSDocInfo(parent);
}
}
return info;
}
/** Find the l-value that the given r-value is being assigned to. */
static Node getBestLValue(Node n) {
Node parent
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>threshold) {
Preconditions.checkState(Thread.currentThread() == startThread);
ThreadTrace trace = getThreadTrace();
// Do nothing if the thread trace was not initialized.
if (!trace.isInitialized()) {
return 0;
}
stopTimeMs = clock.currentTimeMillis();
if (extraTracingValues != null) {
// We use extraTracingValues.length rather than
// extraTracingStatistics.size() because a new statistic may
// have been added
for (int i = 0; i < extraTracingValues.length; i++) {
long value = extraTracingStatistics.get(i).stop(startThread);
extraTracingValues[i] = value - extraTracingValues[i];
}
}
// Do nothing if the thread trace was not initialized.
if (!trace.isInitialized()) {
return 0;
}
trace.endEvent(this, silence_threshold);
return stopTimeMs - startTimeMs;
}
/** Stop the trace using the default silence_threshold
*
* @return The time that this trace actually ran.
*/
long stop() {
return stop(-1);
}
@Override public String toString() {
if (type == null) {
return comment;
} else {
return "[" + type + "] " + comment;
}
}
static void setDefaultSilenceThreshold(int threshold) {
getThreadTrace().defaultSilenceThreshold = threshold;
}
/**
* Initialize the trace associated with the current thread by clearing
* out any existing trace. There shouldn't be a trace so if one is
* found we log it as an error.
*/
static void initCurrentThreadTrace() {
ThreadTrace events = getThreadTrace();
if (!events.isEmpty()) {
logger.log(Level.WARNING,
"Non-empty timer log:\n" + events,
new Throwable());
clearThreadTrace();
// Grab a new thread trace if we find a previous non-empty ThreadTrace.
events = getThreadTrace();
}
// Mark the thread trace as initialized.
events.init();
}
static void initCurrentThreadTrace(int default_silence_threshold) {
initCurrentThreadTrace();
setDefaultSilenceThreshold(default_silence_threshold);
}
/**
* Returns a timer report similar to the one
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>AlreadyOutstanding = outstandingEvents.add(t);
Preconditions.checkState(notAlreadyOutstanding);
}
/**
* Called by {@link Tracer#stop()} to create a stop event.
*/
void endEvent(Tracer t, int silenceThreshold) {
boolean wasOutstanding = outstandingEvents.remove(t);
if (!wasOutstanding) {
if (isOutstandingEventsTruncated) {
// The events stack overflowed and was truncated, so just log a
// warning. Otherwise, we get an exception which is extremely
// confusing.
logger.log(Level.WARNING,
"event not found, probably because the event stack "
+ "overflowed and was truncated",
new Throwable());
} else {
// throw an exception if the event was not found and the events stack
// is pristine
throw new IllegalStateException();
}
}
long elapsed = t.stopTimeMs - t.startTimeMs;
if (silenceThreshold == -1) { // use default
silenceThreshold = defaultSilenceThreshold;
}
if (elapsed < silenceThreshold) {
// If this one is silent then we need to remove the start Event
boolean removed = false;
for (int i = 0; i < events.size(); i++) {
Event e = events.get(i);
if (e.tracer == t) {
Preconditions.checkState(e.isStart);
events.remove(i);
removed = true;
break;
}
}
// Only assert if we didn't find the original and the events
// weren't truncated.
Preconditions.checkState(removed || isEventsTruncated);
} else {
events.add(new Event(false, t));
}
if (t.type != null) {
Stat stat = stats.get(t.type);
if (stat == null) {
stat = new Stat();
if (!extraTracingStatistics.isEmpty()) {
stat.extraInfo = new int[extraTracingStatistics.size()];
}
stats.put(t.type, stat);
}
stat.count++;
if (typeToCountMap != null) {
typeToCountMap.incrementBy(t.type, 1);
}
stat.clockTime += elapsed;
if (typeToTimeMap != null)
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> non-controversial. #3 is
* a bit trickier. It means that if you have:
* <code>
* /** @param {number} x /
* Foo.prototype.bar = goog.abstractMethod;
* </code>
* the JSDocInfo will appear in two places in the type system: in the 'bar'
* slot of Foo.prototype, and on the function expression type created by
* this expression.
*
* @author nicksantos@google.com (Nick Santos)
*/
class InferJSDocInfo extends AbstractPostOrderCallback
implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
@SuppressWarnings("unused")
private boolean inExterns;
InferJSDocInfo(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
if (externs != null) {
inExterns = true;
NodeTraversal.traverse(compiler, externs, this);
}
if (root != null) {
inExterns = false;
NodeTraversal.traverse(compiler, root, this);
}
}
@Override
public void hotSwapScript(Node root, Node originalRoot) {
Preconditions.checkNotNull(root);
Preconditions.checkState(root.isScript());
inExterns = false;
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
JSDocInfo docInfo;
switch (n.getType()) {
// Infer JSDocInfo on types of all type declarations on variables.
case Token.NAME:
if (parent == null) {
return;
}
// Only allow JSDoc on VARs, function declarations, and assigns.
if (!parent.isVar() &&
!NodeUtil.isFunctionDeclaration(parent) &&
!(parent.isAssign() &&
n == parent.getFirstChild())) {
return;
}
// There are four places the doc info could live.
// 1) A FUNCTION node.
// /** ... */ function f() { ... }
// 2) An ASSIGN parent.
// /** ... */ x = function () { ... }
//
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> code identical to how it's been for years.
this.outputCharsetEncoder = null;
} else {
this.outputCharsetEncoder = outputCharset.newEncoder();
}
}
CodeGenerator(CodeConsumer consumer) {
this(consumer, null);
}
/**
* Insert a ECMASCRIPT 5 strict annotation.
*/
public void tagAsStrict() {
add("'use strict';");
}
void add(String str) {
cc.add(str);
}
private void addIdentifier(String identifier) {
cc.addIdentifier(identifierEscape(identifier));
}
void add(Node n) {
add(n, Context.OTHER);
}
void add(Node n, Context context) {
if (!cc.continueProcessing()) {
return;
}
int type = n.getType();
String opstr = NodeUtil.opToStr(type);
int childCount = n.getChildCount();
Node first = n.getFirstChild();
Node last = n.getLastChild();
// Handle all binary operators
if (opstr != null && first != last) {
Preconditions.checkState(
childCount == 2,
"Bad binary operator \"%s\": expected 2 arguments but got %s",
opstr, childCount);
int p = NodeUtil.precedence(type);
// For right-hand-side of operations, only pass context if it's
// the IN_FOR_INIT_CLAUSE one.
Context rhsContext = getContextForNoInOperator(context);
// Handle associativity.
// e.g. if the parse tree is a * (b * c),
// we can simply generate a * b * c.
if (last.getType() == type &&
NodeUtil.isAssociative(type)) {
addExpr(first, p, context);
cc.addOp(opstr, true);
addExpr(last, p, rhsContext);
} else if (NodeUtil.isAssignmentOp(n) && NodeUtil.isAssignmentOp(last)) {
// Assignments are the only right-associative binary operators
addExpr(first, p, context);
cc.addOp(opstr, true);
addExpr(last, p, rhsContext);
} else {
unrollBinaryOperator(n, type
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, opstr, context, rhsContext, p, p + 1);
}
return;
}
cc.startSourceMapping(n);
switch (type) {
case Token.TRY: {
Preconditions.checkState(first.getNext().isBlock() &&
!first.getNext().hasMoreThanOneChild());
Preconditions.checkState(childCount >= 2 && childCount <= 3);
add("try");
add(first, Context.PRESERVE_BLOCK);
// second child contains the catch block, or nothing if there
// isn't a catch block
Node catchblock = first.getNext().getFirstChild();
if (catchblock != null) {
add(catchblock);
}
if (childCount == 3) {
add("finally");
add(last, Context.PRESERVE_BLOCK);
}
break;
}
case Token.CATCH:
Preconditions.checkState(childCount == 2);
add("catch(");
add(first);
add(")");
add(last, Context.PRESERVE_BLOCK);
break;
case Token.THROW:
Preconditions.checkState(childCount == 1);
add("throw");
add(first);
// Must have a ';' after a throw statement, otherwise safari can't
// parse this.
cc.endStatement(true);
break;
case Token.RETURN:
add("return");
if (childCount == 1) {
add(first);
} else {
Preconditions.checkState(childCount == 0);
}
cc.endStatement();
break;
case Token.VAR:
if (first != null) {
add("var ");
addList(first, false, getContextForNoInOperator(context));
}
break;
case Token.LABEL_NAME:
Preconditions.checkState(!n.getString().isEmpty());
addIdentifier(n.getString());
break;
case Token.NAME:
if (first == null || first.isEmpty()) {
addIdentifier(n.getString());
} else {
Preconditions.checkState(childCount == 1);
addIdentifier(n.getString());
cc.addOp("=", true);
if (first.isComma()) {
addExpr(first, NodeUtil.precedence(Token.ASSIGN), Context.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>OTHER);
} else {
// Add expression, consider nearby code at lowest level of
// precedence.
addExpr(first, 0, getContextForNoInOperator(context));
}
}
break;
case Token.ARRAYLIT:
add("[");
addArrayList(first);
add("]");
break;
case Token.PARAM_LIST:
add("(");
addList(first);
add(")");
break;
case Token.COMMA:
Preconditions.checkState(childCount == 2);
unrollBinaryOperator(n, Token.COMMA, ",", context, Context.OTHER, 0, 0);
break;
case Token.NUMBER:
Preconditions.checkState(childCount == 0);
cc.addNumber(n.getDouble());
break;
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS: {
// All of these unary operators are right-associative
Preconditions.checkState(childCount == 1);
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type), Context.OTHER);
break;
}
case Token.NEG: {
Preconditions.checkState(childCount == 1);
// It's important to our sanity checker that the code
// we print produces the same AST as the code we parse back.
// NEG is a weird case because Rhino parses "- -2" as "2".
if (n.getFirstChild().isNumber()) {
cc.addNumber(-n.getFirstChild().getDouble());
} else {
cc.addOp(NodeUtil.opToStrNoFail(type), false);
addExpr(first, NodeUtil.precedence(type), Context.OTHER);
}
break;
}
case Token.HOOK: {
Preconditions.checkState(childCount == 3);
int p = NodeUtil.precedence(type);
addExpr(first, p + 1, context);
cc.addOp("?", true);
addExpr(first.getNext(), 1, Context.OTHER);
cc.addOp(":", true);
addExpr(last, 1, Context.OTHER);
break;
}
case Token
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.REGEXP:
if (!first.isString() ||
!last.isString()) {
throw new Error("Expected children to be strings");
}
String regexp = regexpEscape(first.getString(), outputCharsetEncoder);
// I only use one .add because whitespace matters
if (childCount == 2) {
add(regexp + last.getString());
} else {
Preconditions.checkState(childCount == 1);
add(regexp);
}
break;
case Token.FUNCTION:
if (n.getClass() != Node.class) {
throw new Error("Unexpected Node subclass.");
}
Preconditions.checkState(childCount == 3);
boolean funcNeedsParens = (context == Context.START_OF_EXPR);
if (funcNeedsParens) {
add("(");
}
add("function");
add(first);
add(first.getNext());
add(last, Context.PRESERVE_BLOCK);
cc.endFunction(context == Context.STATEMENT);
if (funcNeedsParens) {
add(")");
}
break;
case Token.GETTER_DEF:
case Token.SETTER_DEF:
Preconditions.checkState(n.getParent().isObjectLit());
Preconditions.checkState(childCount == 1);
Preconditions.checkState(first.isFunction());
// Get methods are unnamed
Preconditions.checkState(first.getFirstChild().getString().isEmpty());
if (type == Token.GETTER_DEF) {
// Get methods have no parameters.
Preconditions.checkState(!first.getChildAtIndex(1).hasChildren());
add("get ");
} else {
// Set methods have one parameter.
Preconditions.checkState(first.getChildAtIndex(1).hasOneChild());
add("set ");
}
// The name is on the GET or SET node.
String name = n.getString();
Node fn = first;
Node parameters = fn.getChildAtIndex(1);
Node body = fn.getLastChild();
// Add the property name.
if (!n.isQuotedString() &&
TokenStream.isJSIdentifier(name) &&
// do not encode literally any non-literal characters that were
// Unicode escaped.
NodeUtil.isLatin(name)) {
add(name);
} else {
//
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Determine if the string is a simple number.
double d = getSimpleNumber(name);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addJsString(n);
}
}
add(parameters);
add(body, Context.PRESERVE_BLOCK);
break;
case Token.SCRIPT:
case Token.BLOCK: {
if (n.getClass() != Node.class) {
throw new Error("Unexpected Node subclass.");
}
boolean preserveBlock = context == Context.PRESERVE_BLOCK;
if (preserveBlock) {
cc.beginBlock();
}
boolean preferLineBreaks =
type == Token.SCRIPT ||
(type == Token.BLOCK &&
!preserveBlock &&
n.getParent() != null &&
n.getParent().isScript());
for (Node c = first; c != null; c = c.getNext()) {
add(c, Context.STATEMENT);
// VAR doesn't include ';' since it gets used in expressions
if (c.isVar()) {
cc.endStatement();
}
if (c.isFunction()) {
cc.maybeLineBreak();
}
// Prefer to break lines in between top-level statements
// because top-level statements are more homogeneous.
if (preferLineBreaks) {
cc.notePreferredLineBreak();
}
}
if (preserveBlock) {
cc.endBlock(cc.breakAfterBlockFor(n, context == Context.STATEMENT));
}
break;
}
case Token.FOR:
if (childCount == 4) {
add("for(");
if (first.isVar()) {
add(first, Context.IN_FOR_INIT_CLAUSE);
} else {
addExpr(first, 0, Context.IN_FOR_INIT_CLAUSE);
}
add(";");
add(first.getNext());
add(";");
add(first.getNext().getNext());
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
} else {
Preconditions.checkState(childCount == 3);
add("for(");
add(first);
add("in");
add(first.getNext());
add(")");
addNonEmptyStatement
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(
last, getContextForNonEmptyExpression(context), false);
}
break;
case Token.DO:
Preconditions.checkState(childCount == 2);
add("do");
addNonEmptyStatement(first, Context.OTHER, false);
add("while(");
add(last);
add(")");
cc.endStatement();
break;
case Token.WHILE:
Preconditions.checkState(childCount == 2);
add("while(");
add(first);
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
break;
case Token.EMPTY:
Preconditions.checkState(childCount == 0);
break;
case Token.GETPROP: {
Preconditions.checkState(
childCount == 2,
"Bad GETPROP: expected 2 children, but got %s", childCount);
Preconditions.checkState(
last.isString(),
"Bad GETPROP: RHS should be STRING");
boolean needsParens = (first.isNumber());
if (needsParens) {
add("(");
}
addExpr(first, NodeUtil.precedence(type), context);
if (needsParens) {
add(")");
}
add(".");
addIdentifier(last.getString());
break;
}
case Token.GETELEM:
Preconditions.checkState(
childCount == 2,
"Bad GETELEM: expected 2 children but got %s", childCount);
addExpr(first, NodeUtil.precedence(type), context);
add("[");
add(first.getNext());
add("]");
break;
case Token.WITH:
Preconditions.checkState(childCount == 2);
add("with(");
add(first);
add(")");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
break;
case Token.INC:
case Token.DEC: {
Preconditions.checkState(childCount == 1);
String o = type == Token.INC ? "++" : "--";
int postProp = n.getIntProp(Node.INCRDECR_PROP);
// A non-zero post-prop value indicates a post inc/dec, default of zero
// is a pre-inc
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>/dec.
if (postProp != 0) {
addExpr(first, NodeUtil.precedence(type), context);
cc.addOp(o, false);
} else {
cc.addOp(o, false);
add(first);
}
break;
}
case Token.CALL:
// We have two special cases here:
// 1) If the left hand side of the call is a direct reference to eval,
// then it must have a DIRECT_EVAL annotation. If it does not, then
// that means it was originally an indirect call to eval, and that
// indirectness must be preserved.
// 2) If the left hand side of the call is a property reference,
// then the call must not a FREE_CALL annotation. If it does, then
// that means it was originally an call without an explicit this and
// that must be preserved.
if (isIndirectEval(first)
|| n.getBooleanProp(Node.FREE_CALL) && NodeUtil.isGet(first)) {
add("(0,");
addExpr(first, NodeUtil.precedence(Token.COMMA), Context.OTHER);
add(")");
} else {
addExpr(first, NodeUtil.precedence(type), context);
}
add("(");
addList(first.getNext());
add(")");
break;
case Token.IF:
boolean hasElse = childCount == 3;
boolean ambiguousElseClause =
context == Context.BEFORE_DANGLING_ELSE && !hasElse;
if (ambiguousElseClause) {
cc.beginBlock();
}
add("if(");
add(first);
add(")");
if (hasElse) {
addNonEmptyStatement(
first.getNext(), Context.BEFORE_DANGLING_ELSE, false);
add("else");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), false);
} else {
addNonEmptyStatement(first.getNext(), Context.OTHER, false);
Preconditions.checkState(childCount == 2);
}
if (ambiguousElseClause) {
cc.endBlock();
}
break;
case Token.NULL:
Preconditions.checkState(childCount == 0);
cc.addConstant("null
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>");
break;
case Token.THIS:
Preconditions.checkState(childCount == 0);
add("this");
break;
case Token.FALSE:
Preconditions.checkState(childCount == 0);
cc.addConstant("false");
break;
case Token.TRUE:
Preconditions.checkState(childCount == 0);
cc.addConstant("true");
break;
case Token.CONTINUE:
Preconditions.checkState(childCount <= 1);
add("continue");
if (childCount == 1) {
if (!first.isLabelName()) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case Token.DEBUGGER:
Preconditions.checkState(childCount == 0);
add("debugger");
cc.endStatement();
break;
case Token.BREAK:
Preconditions.checkState(childCount <= 1);
add("break");
if (childCount == 1) {
if (!first.isLabelName()) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(" ");
add(first);
}
cc.endStatement();
break;
case Token.EXPR_RESULT:
Preconditions.checkState(childCount == 1);
add(first, Context.START_OF_EXPR);
cc.endStatement();
break;
case Token.NEW:
add("new ");
int precedence = NodeUtil.precedence(type);
// If the first child contains a CALL, then claim higher precedence
// to force parentheses. Otherwise, when parsed, NEW will bind to the
// first viable parentheses (don't traverse into functions).
if (NodeUtil.containsType(
first, Token.CALL, NodeUtil.MATCH_NOT_FUNCTION)) {
precedence = NodeUtil.precedence(first.getType()) + 1;
}
addExpr(first, precedence, Context.OTHER);
// '()' is optional when no arguments are present
Node next = first.getNext();
if (next != null) {
add("(");
addList(next);
add(")");
}
break;
case Token.STRING_KEY:
Preconditions.checkState(
child
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Count == 1, "Object lit key must have 1 child");
addJsString(n);
break;
case Token.STRING:
Preconditions.checkState(
childCount == 0, "A string may not have children");
addJsString(n);
break;
case Token.DELPROP:
Preconditions.checkState(childCount == 1);
add("delete ");
add(first);
break;
case Token.OBJECTLIT: {
boolean needsParens = (context == Context.START_OF_EXPR);
if (needsParens) {
add("(");
}
add("{");
for (Node c = first; c != null; c = c.getNext()) {
if (c != first) {
cc.listSeparator();
}
if (c.isGetterDef() || c.isSetterDef()) {
add(c);
} else {
Preconditions.checkState(c.isStringKey());
String key = c.getString();
// Object literal property names don't have to be quoted if they
// are not JavaScript keywords
if (!c.isQuotedString() &&
!TokenStream.isKeyword(key) &&
TokenStream.isJSIdentifier(key) &&
// do not encode literally any non-literal characters that
// were Unicode escaped.
NodeUtil.isLatin(key)) {
add(key);
} else {
// Determine if the string is a simple number.
double d = getSimpleNumber(key);
if (!Double.isNaN(d)) {
cc.addNumber(d);
} else {
addExpr(c, 1, Context.OTHER);
}
}
add(":");
addExpr(c.getFirstChild(), 1, Context.OTHER);
}
}
add("}");
if (needsParens) {
add(")");
}
break;
}
case Token.SWITCH:
add("switch(");
add(first);
add(")");
cc.beginBlock();
addAllSiblings(first.getNext());
cc.endBlock(context == Context.STATEMENT);
break;
case Token.CASE:
Preconditions.checkState(childCount == 2);
add("case ");
add(first);
addCaseBody(last);
break;
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
case Token.DEFAULT_CASE:
Preconditions.checkState(childCount == 1);
add("default");
addCaseBody(first);
break;
case Token.LABEL:
Preconditions.checkState(childCount == 2);
if (!first.isLabelName()) {
throw new Error("Unexpected token type. Should be LABEL_NAME.");
}
add(first);
add(":");
addNonEmptyStatement(
last, getContextForNonEmptyExpression(context), true);
break;
default:
throw new Error("Unknown type " + type + "\n" + n.toStringTree());
}
cc.endSourceMapping(n);
}
/**
* We could use addList recursively here, but sometimes we produce
* very deeply nested operators and run out of stack space, so we
* just unroll the recursion when possible.
*
* We assume nodes are left-recursive.
*/
private void unrollBinaryOperator(
Node n, int op, String opStr, Context context,
Context rhsContext, int leftPrecedence, int rightPrecedence) {
Node firstNonOperator = n.getFirstChild();
while (firstNonOperator.getType() == op) {
firstNonOperator = firstNonOperator.getFirstChild();
}
addExpr(firstNonOperator, leftPrecedence, context);
Node current = firstNonOperator;
do {
current = current.getParent();
cc.addOp(opStr, true);
addExpr(current.getFirstChild().getNext(), rightPrecedence, rhsContext);
} while (current != n);
}
static boolean isSimpleNumber(String s) {
int len = s.length();
for (int index = 0; index < len; index++) {
char c = s.charAt(index);
if (c < '0' || c > '9') {
return false;
}
}
return len > 0 && s.charAt(0) != '0';
}
static double getSimpleNumber(String s) {
if (isSimpleNumber(s)) {
try {
long l = Long.parseLong(s);
if (l < NodeUtil.MAX_POSITIVE_INTEGER_NUMBER) {
return l;
}
} catch (NumberFormatException e) {
// The number was
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>} if the variable is declared as a constant,
* based on the value reported by {@code NodeUtil}.
*/
public boolean isConst() {
return nameNode != null && NodeUtil.isConstantName(nameNode);
}
/**
* Returns {@code true} if the variable is declared as a define.
* A variable is a define if it is annotated by {@code @define}.
*/
public boolean isDefine() {
return isDefine;
}
public Node getInitialValue() {
Node parent = getParentNode();
int pType = parent.getType();
if (pType == Token.FUNCTION) {
return parent;
} else if (pType == Token.ASSIGN) {
return parent.getLastChild();
} else if (pType == Token.VAR) {
return nameNode.getFirstChild();
} else {
return null;
}
}
/**
* Gets this variable's type. To know whether this type has been inferred,
* see {@code #isTypeInferred()}.
*/
@Override
public JSType getType() {
return type;
}
/**
* Returns the name node that produced this variable.
*/
public Node getNameNode() {
return nameNode;
}
/**
* Gets the JSDocInfo for the variable.
*/
@Override
public JSDocInfo getJSDocInfo() {
return info;
}
/**
* Sets this variable's type.
* @throws IllegalStateException if the variable's type is not inferred
*/
void setType(JSType type) {
Preconditions.checkState(isTypeInferred());
this.type = type;
}
/**
* Resolve this variable's type.
*/
void resolveType(ErrorReporter errorReporter) {
if (type != null) {
type = type.resolve(errorReporter, scope);
}
}
/**
* Returns whether this variable's type is inferred. To get the variable's
* type, see {@link #getType()}.
*/
@Override
public boolean isTypeInferred() {
return typeInferred;
}
public String getInputName() {
if (input == null)
return "<non-file>";
else
return input.getName();
}
public boolean isNoShadow() {
if (info != null && info
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.isNoShadow()) {
return true;
} else {
return false;
}
}
@Override public boolean equals(Object other) {
if (!(other instanceof Var)) {
return false;
}
Var otherVar = (Var) other;
return otherVar.nameNode == nameNode;
}
@Override public int hashCode() {
return nameNode.hashCode();
}
@Override
public String toString() {
return "Scope.Var " + name + "{" + type + "}";
}
/** Record that this is escaped by an inner scope. */
void markEscaped() {
markedEscaped = true;
}
/**
* Whether this is escaped by an inner scope.
* Notice that not all scope creators record this information.
*/
boolean isMarkedEscaped() {
return markedEscaped;
}
}
/**
* A special subclass of Var used to distinguish "arguments" in the current
* scope.
*/
// TODO(johnlenz): Include this the list of Vars for the scope.
public static class Arguments extends Var {
Arguments(Scope scope) {
super(
false, // no inferred
"arguments", // always arguments
null, // no declaration node
// TODO(johnlenz): provide the type of "Arguments".
null, // no type info
scope,
-1, // no variable index
null, // input,
false, // not a define
null // no jsdoc
);
}
@Override public boolean equals(Object other) {
if (!(other instanceof Arguments)) {
return false;
}
Arguments otherVar = (Arguments) other;
return otherVar.scope.getRootNode() == scope.getRootNode();
}
@Override public int hashCode() {
return System.identityHashCode(this);
}
}
/**
* Creates a Scope given the parent Scope and the root node of the scope.
* @param parent The parent Scope. Cannot be null.
* @param rootNode Typically the FUNCTION node.
*/
Scope(Scope parent, Node rootNode) {
Preconditions.checkNotNull(parent);
Preconditions.checkArgument(rootNode != parent.rootNode);
this.parent = parent;
this.rootNode = rootNode;
JSType nodeType = rootNode
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
* @param type the variable's type
* @param input the input in which this variable is defined.
*/
Var declare(String name, Node nameNode, JSType type, CompilerInput input) {
return declare(name, nameNode, type, input, true);
}
/**
* Declares a variable.
*
* @param name name of the variable
* @param nameNode the NAME node declaring the variable
* @param type the variable's type
* @param input the input in which this variable is defined.
* @param inferred Whether this variable's type is inferred (as opposed
* to declared).
*/
Var declare(String name, Node nameNode,
JSType type, CompilerInput input, boolean inferred) {
Preconditions.checkState(name != null && name.length() > 0);
// Make sure that it's declared only once
Preconditions.checkState(vars.get(name) == null);
// native variables do not have a name node.
JSDocInfo info = nameNode == null
? null : NodeUtil.getBestJSDocInfo(nameNode);
Var var = new Var(inferred, name, nameNode, type, this, vars.size(), input,
info != null && info.isDefine(), info);
vars.put(name, var);
return var;
}
/**
* Undeclares a variable, to be used when the compiler optimizes out
* a variable and removes it from the scope.
*/
void undeclare(Var var) {
Preconditions.checkState(var.scope == this);
Preconditions.checkState(vars.get(var.name) == var);
vars.remove(var.name);
}
@Override
public StaticSlot<JSType> getSlot(String name) {
return getVar(name);
}
@Override
public StaticSlot<JSType> getOwnSlot(String name) {
return vars.get(name);
}
/**
* Returns the variable, may be null
*/
public Var getVar(String name) {
Var var = vars.get(name);
if (var != null) {
return var;
} else if (parent != null) { // Recurse up the parent Scope
return parent.getVar(name);
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> are duplicates of
// one another.
int currentIndex = 0;
Iterator<JSType> it = alternates.iterator();
while (it.hasNext()) {
JSType current = it.next();
// Unknown and NoResolved types may just be names that haven't
// been resolved yet. So keep these in the union, and just use
// equality checking for simple de-duping.
if (alternate.isUnknownType() ||
current.isUnknownType() ||
alternate.isNoResolvedType() ||
current.isNoResolvedType() ||
alternate.hasAnyTemplate() ||
current.hasAnyTemplate()) {
if (alternate.isEquivalentTo(current)) {
// Alternate is unnecessary.
return this;
}
} else {
if (alternate.isSubtype(current)) {
// Alternate is unnecessary.
return this;
} else if (current.isSubtype(alternate)) {
// Alternate makes current obsolete
it.remove();
if (currentIndex == functionTypePosition) {
functionTypePosition = -1;
} else if (currentIndex < functionTypePosition) {
functionTypePosition--;
currentIndex--;
}
}
}
currentIndex++;
}
if (alternate.isFunctionType()) {
// See the comments on functionTypePosition above.
Preconditions.checkState(functionTypePosition == -1);
functionTypePosition = alternates.size();
}
alternates.add(alternate);
result = null; // invalidate the memoized result
}
} else {
result = null;
}
return this;
}
/**
* Reduce the alternates into a non-union type.
* If the alternates can't be accurately represented with a non-union
* type, return null.
*/
private JSType reduceAlternatesWithoutUnion() {
if (isAllType) {
return registry.getNativeType(ALL_TYPE);
} else if (isNativeUnknownType) {
if (areAllUnknownsChecked) {
return registry.getNativeType(CHECKED_UNKNOWN_TYPE);
} else {
return registry.getNativeType(UNKNOWN_TYPE);
}
} else {
int size = alternates.size();
if (size > maxUnionSize) {
return registry.getNativeType(UNKNOWN_TYPE);
} else
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
Preconditions.checkNotNull(registerFunction);
return this.registerFunction == registerFunction;
}
boolean isGetterFunction() {
return registerFunction != null;
}
String getName() {
return name;
}
String getExpectedTypeName() {
return expectedTypeName;
}
Node createDefaultValueNode() {
switch (this) {
case REGISTER_BOOLEAN:
return IR.falseNode();
case REGISTER_NUMBER:
return IR.number(0);
case REGISTER_STRING:
return IR.string("");
}
throw new IllegalStateException();
}
}
// A map of function name -> TweakFunction.
private static final Map<String, TweakFunction> TWEAK_FUNCTIONS_MAP;
static {
TWEAK_FUNCTIONS_MAP = Maps.newHashMap();
for (TweakFunction func : TweakFunction.values()) {
TWEAK_FUNCTIONS_MAP.put(func.getName(), func);
}
}
ProcessTweaks(AbstractCompiler compiler, boolean stripTweaks,
Map<String, Node> compilerDefaultValueOverrides) {
this.compiler = compiler;
this.stripTweaks = stripTweaks;
// Having the map sorted is required for the unit tests to be deterministic.
this.compilerDefaultValueOverrides = Maps.newTreeMap();
this.compilerDefaultValueOverrides.putAll(compilerDefaultValueOverrides);
}
@Override
public void process(Node externs, Node root) {
CollectTweaksResult result = collectTweaks(root);
applyCompilerDefaultValueOverrides(result.tweakInfos);
boolean changed = false;
if (stripTweaks) {
changed = stripAllCalls(result.tweakInfos);
} else if (!compilerDefaultValueOverrides.isEmpty()) {
changed = replaceGetCompilerOverridesCalls(result.getOverridesCalls);
}
if (changed) {
compiler.reportCodeChange();
}
}
/**
* Passes the compiler default value overrides to the JS by replacing calls
* to goog.tweak.getCompilerOverrids_ with a map of tweak ID->default value;
*/
private boolean replaceGetCompilerOverridesCalls(
List<TweakFunctionCall> calls) {
for (TweakFunctionCall call : calls) {
Node callNode = call.callNode;
Node obj
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(String sourceName, TweakFunction tweakFunc,
Node callNode) {
functionCalls.add(new TweakFunctionCall(sourceName, tweakFunc, callNode));
}
boolean isRegistered() {
return registerCall != null;
}
Node getDefaultValueNode() {
Preconditions.checkState(isRegistered());
// Use calls to goog.tweak.overrideDefaultValue() first.
if (defaultValueNode != null) {
return defaultValueNode;
}
// Use the value passed to the register function next.
if (registerCall.valueNode != null) {
return registerCall.valueNode;
}
// Otherwise, use the default value for the tweak's type.
return registerCall.tweakFunc.createDefaultValueNode();
}
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> {
Preconditions.checkNotNull(errorRoot);
this.fnName = fnName == null ? "" : fnName;
this.codingConvention = compiler.getCodingConvention();
this.typeRegistry = compiler.getTypeRegistry();
this.errorRoot = errorRoot;
this.sourceName = sourceName;
this.compiler = compiler;
this.scope = scope;
}
/**
* Sets the contents of this function.
*/
FunctionTypeBuilder setContents(@Nullable FunctionContents contents) {
if (contents != null) {
this.contents = contents;
}
return this;
}
/**
* Infer the parameter and return types of a function from
* the parameter and return types of the function it is overriding.
*
* @param oldType The function being overridden. Does nothing if this is null.
* @param paramsParent The LP node of the function that we're assigning to.
* If null, that just means we're not initializing this to a function
* literal.
*/
FunctionTypeBuilder inferFromOverriddenFunction(
@Nullable FunctionType oldType, @Nullable Node paramsParent) {
if (oldType == null) {
return this;
}
returnType = oldType.getReturnType();
returnTypeInferred = oldType.isReturnTypeInferred();
if (paramsParent == null) {
// Not a function literal.
parametersNode = oldType.getParametersNode();
if (parametersNode == null) {
parametersNode = new FunctionParamBuilder(typeRegistry).build();
}
} else {
// We're overriding with a function literal. Apply type information
// to each parameter of the literal.
FunctionParamBuilder paramBuilder =
new FunctionParamBuilder(typeRegistry);
Iterator<Node> oldParams = oldType.getParameters().iterator();
boolean warnedAboutArgList = false;
boolean oldParamsListHitOptArgs = false;
for (Node currentParam = paramsParent.getFirstChild();
currentParam != null; currentParam = currentParam.getNext()) {
if (oldParams.hasNext()) {
Node oldParam = oldParams.next();
Node newParam = paramBuilder.newParameterFromNode(oldParam);
oldParamsListHitOptArgs = oldParamsListHitOptArgs ||
oldParam.isVarArgs() ||
oldParam.isOptionalArg();
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> the super type resolves.
* @param objectType
* @return true if objectType is resolvable in the future
*/
private static boolean hasMoreTagsToResolve(ObjectType objectType) {
Preconditions.checkArgument(objectType.isUnknownType());
if (objectType.getImplicitPrototype() != null) {
// constructor extends class
if (objectType.getImplicitPrototype().isResolved()) {
return false;
} else {
return true;
}
} else {
// interface extends interfaces
FunctionType ctor = objectType.getConstructor();
if (ctor != null) {
for (ObjectType interfaceType : ctor.getExtendedInterfaces()) {
if (!interfaceType.isResolved()) {
return true;
}
}
}
return false;
}
}
/** Holds data dynamically inferred about functions. */
static interface FunctionContents {
/** Returns the source node of this function. May be null. */
Node getSourceNode();
/** Returns if the function may be in externs. */
boolean mayBeFromExterns();
/** Returns if a return of a real value (not undefined) appears. */
boolean mayHaveNonEmptyReturns();
/** Returns if this consists of a single throw. */
boolean mayHaveSingleThrow();
/** Gets a list of variables in this scope that are escaped. */
Iterable<String> getEscapedVarNames();
/** Gets a list of variables whose properties are escaped. */
Set<String> getEscapedQualifiedNames();
}
static class UnknownFunctionContents implements FunctionContents {
private static UnknownFunctionContents singleton =
new UnknownFunctionContents();
static FunctionContents get() {
return singleton;
}
@Override
public Node getSourceNode() {
return null;
}
@Override
public boolean mayBeFromExterns() {
return true;
}
@Override
public boolean mayHaveNonEmptyReturns() {
return true;
}
@Override
public boolean mayHaveSingleThrow() {
return true;
}
@Override
public Iterable<String> getEscapedVarNames() {
return ImmutableList.of();
}
@Override
public Set<String> getEscapedQualifiedNames() {
return ImmutableSet.of();
}
}
static class AstFunctionContents implements FunctionContents {
private final Node n;
private boolean
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> /**
* Creates a pass to check global name references at the given warning level.
*/
CheckGlobalNames(AbstractCompiler compiler, CheckLevel level) {
this.compiler = compiler;
this.convention = compiler.getCodingConvention();
this.level = level;
}
/**
* Injects a pre-computed global namespace, so that the same namespace
* can be re-used for multiple check passes. Returns this for easy chaining.
*/
CheckGlobalNames injectNamespace(GlobalNamespace namespace) {
Preconditions.checkArgument(namespace.hasExternsRoot());
this.namespace = namespace;
return this;
}
@Override
public void process(Node externs, Node root) {
if (namespace == null) {
namespace = new GlobalNamespace(compiler, externs, root);
}
// Find prototype properties that will affect our analysis.
Preconditions.checkState(namespace.hasExternsRoot());
findPrototypeProps("Object", objectPrototypeProps);
findPrototypeProps("Function", functionPrototypeProps);
objectPrototypeProps.addAll(
convention.getIndirectlyDeclaredProperties());
for (Name name : namespace.getNameForest()) {
// Skip extern names. Externs are often not runnable as real code,
// and will do things like:
// var x;
// x.method;
// which this check forbids.
if (name.inExterns) {
continue;
}
checkDescendantNames(name, name.globalSets + name.localSets > 0);
}
}
private void findPrototypeProps(String type, Set<String> props) {
Name slot = namespace.getSlot(type);
if (slot != null) {
for (Ref ref : slot.getRefs()) {
if (ref.type == Ref.Type.PROTOTYPE_GET) {
Node fullName = ref.getNode().getParent().getParent();
if (fullName.isGetProp()) {
props.add(fullName.getLastChild().getString());
}
}
}
}
}
/**
* Checks to make sure all the descendants of a name are defined if they
* are referenced.
*
* @param name A global name.
* @param nameIsDefined If true, {@code name} is defined. Otherwise, it's
* undefined, and any
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>".equals(callName.getQualifiedName()) &&
n.getChildCount() >= 3) {
Node typeArray = callName.getNext().getNext();
if (typeArray.isArrayLit()) {
List<String> typeNames = Lists.newArrayList();
for (Node name = typeArray.getFirstChild(); name != null;
name = name.getNext()) {
if (name.isString()) {
typeNames.add(name.getString());
}
}
return typeNames;
}
}
return super.identifyTypeDeclarationCall(n);
}
@Override
public String getAbstractMethodName() {
return "goog.abstractMethod";
}
@Override
public String getSingletonGetterClassName(Node callNode) {
Node callArg = callNode.getFirstChild();
String callName = callArg.getQualifiedName();
// Use both the original name and the post-CollapseProperties name.
if (!("goog.addSingletonGetter".equals(callName) ||
"goog$addSingletonGetter".equals(callName)) ||
callNode.getChildCount() != 2) {
return super.getSingletonGetterClassName(callNode);
}
return callArg.getNext().getQualifiedName();
}
@Override
public void applySingletonGetter(FunctionType functionType,
FunctionType getterType, ObjectType objectType) {
super.applySingletonGetter(functionType, getterType, objectType);
functionType.defineDeclaredProperty("getInstance", getterType,
functionType.getSource());
functionType.defineDeclaredProperty("instance_", objectType,
functionType.getSource());
}
@Override
public String getGlobalObject() {
return "goog.global";
}
private final Set<String> propertyTestFunctions = ImmutableSet.of(
"goog.isDef", "goog.isNull", "goog.isDefAndNotNull",
"goog.isString", "goog.isNumber", "goog.isBoolean",
"goog.isFunction", "goog.isArray", "goog.isObject");
@Override
public boolean isPropertyTestFunction(Node call) {
Preconditions.checkArgument(call.isCall());
return propertyTestFunctions.contains(
call.getFirstChild().getQualifiedName()) ||
super.isPropertyTestFunction(call);
}
@Override
public ObjectLiteralCast getObjectLiteralCast(Node callNode) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> Preconditions.checkArgument(callNode.isCall());
ObjectLiteralCast proxyCast = super.getObjectLiteralCast(callNode);
if (proxyCast != null) {
return proxyCast;
}
Node callName = callNode.getFirstChild();
if (!"goog.reflect.object".equals(callName.getQualifiedName()) ||
callNode.getChildCount() != 3) {
return null;
}
Node typeNode = callName.getNext();
if (!typeNode.isQualifiedName()) {
return null;
}
Node objectNode = typeNode.getNext();
if (!objectNode.isObjectLit()) {
return new ObjectLiteralCast(null, null, OBJECTLIT_EXPECTED);
}
return new ObjectLiteralCast(
typeNode.getQualifiedName(), typeNode.getNext(), null);
}
@Override
public boolean isOptionalParameter(Node parameter) {
return false;
}
@Override
public boolean isVarArgsParameter(Node parameter) {
return false;
}
@Override
public boolean isPrivate(String name) {
return false;
}
@Override
public Collection<AssertionFunctionSpec> getAssertionFunctions() {
return ImmutableList.<AssertionFunctionSpec>of(
new AssertionFunctionSpec("goog.asserts.assert"),
new AssertionFunctionSpec("goog.asserts.assertNumber",
JSTypeNative.NUMBER_TYPE),
new AssertionFunctionSpec("goog.asserts.assertString",
JSTypeNative.STRING_TYPE),
new AssertionFunctionSpec("goog.asserts.assertFunction",
JSTypeNative.FUNCTION_INSTANCE_TYPE),
new AssertionFunctionSpec("goog.asserts.assertObject",
JSTypeNative.OBJECT_TYPE),
new AssertionFunctionSpec("goog.asserts.assertArray",
JSTypeNative.ARRAY_TYPE),
new AssertInstanceofSpec("goog.asserts.assertInstanceof")
);
}
@Override
public Bind describeFunctionBind(Node n, boolean useTypeInfo) {
Bind result = super.describeFunctionBind(n, useTypeInfo);
if (result != null) {
return result;
}
if (!n.isCall()) {
return null;
}
Node callTarget = n.getFirstChild();
String name = callTarget.getQualifiedName();
if (name != null) {
if (name.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>PropListItem {
private static final long serialVersionUID = 1L;
private final Object objectValue;
ObjectPropListItem(int propType, Object objectValue, PropListItem next) {
super(propType, next);
this.objectValue = objectValue;
}
@Override
public int getIntValue() {
throw new UnsupportedOperationException();
}
@Override
public Object getObjectValue() {
return objectValue;
}
@Override
public String toString() {
return objectValue == null ? "null" : objectValue.toString();
}
@Override
public PropListItem chain(PropListItem next) {
return new ObjectPropListItem(getType(), objectValue, next);
}
}
// A base class for int storing props
private static class IntPropListItem extends AbstractPropListItem {
private static final long serialVersionUID = 1L;
final int intValue;
IntPropListItem(int propType, int intValue, PropListItem next) {
super(propType, next);
this.intValue = intValue;
}
@Override
public int getIntValue() {
return intValue;
}
@Override
public Object getObjectValue() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return String.valueOf(intValue);
}
@Override
public PropListItem chain(PropListItem next) {
return new IntPropListItem(getType(), intValue, next);
}
}
public Node(int nodeType) {
type = nodeType;
parent = null;
sourcePosition = -1;
}
public Node(int nodeType, Node child) {
Preconditions.checkArgument(child.parent == null,
"new child has existing parent");
Preconditions.checkArgument(child.next == null,
"new child has existing sibling");
type = nodeType;
parent = null;
first = last = child;
child.next = null;
child.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, Node left, Node right) {
Preconditions.checkArgument(left.parent == null,
"first new child has existing parent");
Preconditions.checkArgument(left.next == null,
"first new child has existing sibling");
Preconditions.checkArgument(right.parent == null,
"second new child has
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> existing parent");
Preconditions.checkArgument(right.next == null,
"second new child has existing sibling");
type = nodeType;
parent = null;
first = left;
last = right;
left.next = right;
left.parent = this;
right.next = null;
right.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, Node left, Node mid, Node right) {
Preconditions.checkArgument(left.parent == null);
Preconditions.checkArgument(left.next == null);
Preconditions.checkArgument(mid.parent == null);
Preconditions.checkArgument(mid.next == null);
Preconditions.checkArgument(right.parent == null);
Preconditions.checkArgument(right.next == null);
type = nodeType;
parent = null;
first = left;
last = right;
left.next = mid;
left.parent = this;
mid.next = right;
mid.parent = this;
right.next = null;
right.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, Node left, Node mid, Node mid2, Node right) {
Preconditions.checkArgument(left.parent == null);
Preconditions.checkArgument(left.next == null);
Preconditions.checkArgument(mid.parent == null);
Preconditions.checkArgument(mid.next == null);
Preconditions.checkArgument(mid2.parent == null);
Preconditions.checkArgument(mid2.next == null);
Preconditions.checkArgument(right.parent == null);
Preconditions.checkArgument(right.next == null);
type = nodeType;
parent = null;
first = left;
last = right;
left.next = mid;
left.parent = this;
mid.next = mid2;
mid.parent = this;
mid2.next = right;
mid2.parent = this;
right.next = null;
right.parent = this;
sourcePosition = -1;
}
public Node(int nodeType, int lineno, int charno) {
type = nodeType;
parent = null;
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node child, int lineno
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, int charno) {
this(nodeType, child);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node left, Node right, int lineno, int charno) {
this(nodeType, left, right);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node left, Node mid, Node right,
int lineno, int charno) {
this(nodeType, left, mid, right);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node left, Node mid, Node mid2, Node right,
int lineno, int charno) {
this(nodeType, left, mid, mid2, right);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node[] children, int lineno, int charno) {
this(nodeType, children);
sourcePosition = mergeLineCharNo(lineno, charno);
}
public Node(int nodeType, Node[] children) {
this.type = nodeType;
parent = null;
if (children.length != 0) {
this.first = children[0];
this.last = children[children.length - 1];
for (int i = 1; i < children.length; i++) {
if (null != children[i - 1].next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
children[i - 1].next = children[i];
Preconditions.checkArgument(children[i - 1].parent == null);
children[i - 1].parent = this;
}
Preconditions.checkArgument(children[children.length - 1].parent == null);
children[children.length - 1].parent = this;
if (null != this.last.next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
}
}
public static Node newNumber(double number) {
return new NumberNode(number);
}
public static Node newNumber(double number, int lineno, int charno) {
return new NumberNode
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(number, lineno, charno);
}
public static Node newString(String str) {
return new StringNode(Token.STRING, str);
}
public static Node newString(int type, String str) {
return new StringNode(type, str);
}
public static Node newString(String str, int lineno, int charno) {
return new StringNode(Token.STRING, str, lineno, charno);
}
public static Node newString(int type, String str, int lineno, int charno) {
return new StringNode(type, str, lineno, charno);
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean hasChildren() {
return first != null;
}
public Node getFirstChild() {
return first;
}
public Node getLastChild() {
return last;
}
public Node getNext() {
return next;
}
public Node getChildBefore(Node child) {
if (child == first) {
return null;
}
Node n = first;
while (n.next != child) {
n = n.next;
if (n == null) {
throw new RuntimeException("node is not a child");
}
}
return n;
}
public Node getChildAtIndex(int i) {
Node n = first;
while (i > 0) {
n = n.next;
i--;
}
return n;
}
public int getIndexOfChild(Node child) {
Node n = first;
int i = 0;
while (n != null) {
if (child == n) {
return i;
}
n = n.next;
i++;
}
return -1;
}
public Node getLastSibling() {
Node n = this;
while (n.next != null) {
n = n.next;
}
return n;
}
public void addChildToFront(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = first;
first = child;
if (last == null) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> last = child;
}
}
public void addChildToBack(Node child) {
Preconditions.checkArgument(child.parent == null);
Preconditions.checkArgument(child.next == null);
child.parent = this;
child.next = null;
if (last == null) {
first = last = child;
return;
}
last.next = child;
last = child;
}
public void addChildrenToFront(Node children) {
for (Node child = children; child != null; child = child.next) {
Preconditions.checkArgument(child.parent == null);
child.parent = this;
}
Node lastSib = children.getLastSibling();
lastSib.next = first;
first = children;
if (last == null) {
last = lastSib;
}
}
public void addChildrenToBack(Node children) {
addChildrenAfter(children, getLastChild());
}
/**
* Add 'child' before 'node'.
*/
public void addChildBefore(Node newChild, Node node) {
Preconditions.checkArgument(node != null && node.parent == this,
"The existing child node of the parent should not be null.");
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
if (first == node) {
newChild.parent = this;
newChild.next = first;
first = newChild;
return;
}
Node prev = getChildBefore(node);
addChildAfter(newChild, prev);
}
/**
* Add 'child' after 'node'.
*/
public void addChildAfter(Node newChild, Node node) {
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
addChildrenAfter(newChild, node);
}
/**
* Add all children after 'node'.
*/
public void addChildrenAfter(Node children, Node node) {
Preconditions.checkArgument(node == null || node.parent == this);
for (Node child = children; child != null; child = child.next) {
Preconditions.checkArgument(child.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>parent == null);
child.parent = this;
}
Node lastSibling = children.getLastSibling();
if (node != null) {
Node oldNext = node.next;
node.next = children;
lastSibling.next = oldNext;
if (node == last) {
last = lastSibling;
}
} else {
// Append to the beginning.
if (first != null) {
lastSibling.next = first;
} else {
last = lastSibling;
}
first = children;
}
}
/**
* Detach a child from its parent and siblings.
*/
public void removeChild(Node child) {
Node prev = getChildBefore(child);
if (prev == null)
first = first.next;
else
prev.next = child.next;
if (child == last) last = prev;
child.next = null;
child.parent = null;
}
/**
* Detaches child from Node and replaces it with newChild.
*/
public void replaceChild(Node child, Node newChild) {
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(child);
newChild.next = child.next;
newChild.parent = this;
if (child == first) {
first = newChild;
} else {
Node prev = getChildBefore(child);
prev.next = newChild;
}
if (child == last)
last = newChild;
child.next = null;
child.parent = null;
}
public void replaceChildAfter(Node prevChild, Node newChild) {
Preconditions.checkArgument(prevChild.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(newChild.next == null,
"The new child node has siblings.");
Preconditions.checkArgument(newChild.parent == null,
"The new child node already has a parent.");
// Copy over important information.
newChild.copyInformationFrom(prevChild);
Node child = prevChild.next;
newChild.next
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> = child.next;
newChild.parent = this;
prevChild.next = newChild;
if (child == last)
last = newChild;
child.next = null;
child.parent = null;
}
@VisibleForTesting
PropListItem lookupProperty(int propType) {
PropListItem x = propListHead;
while (x != null && propType != x.getType()) {
x = x.getNext();
}
return x;
}
/**
* Clone the properties from the provided node without copying
* the property object. The receiving node may not have any
* existing properties.
* @param other The node to clone properties from.
* @return this node.
*/
public Node clonePropsFrom(Node other) {
Preconditions.checkState(this.propListHead == null,
"Node has existing properties.");
this.propListHead = other.propListHead;
return this;
}
public void removeProp(int propType) {
PropListItem result = removeProp(propListHead, propType);
if (result != propListHead) {
propListHead = result;
}
}
/**
* @param item The item to inspect
* @param propType The property to look for
* @return The replacement list if the property was removed, or
* 'item' otherwise.
*/
private PropListItem removeProp(PropListItem item, int propType) {
if (item == null) {
return null;
} else if (item.getType() == propType) {
return item.getNext();
} else {
PropListItem result = removeProp(item.getNext(), propType);
if (result != item.getNext()) {
return item.chain(result);
} else {
return item;
}
}
}
public Object getProp(int propType) {
if (propType == SOURCENAME_PROP) {
return getSourceFileName();
}
PropListItem item = lookupProperty(propType);
if (item == null) {
return null;
}
return item.getObjectValue();
}
public boolean getBooleanProp(int propType) {
return getIntProp(propType) != 0;
}
/**
* Returns the integer value for the property, or
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> * for (Node sibling : n.siblings()) { ...</pre>
*/
public Iterable<Node> siblings() {
return new SiblingNodeIterable(this);
}
/**
* @see Node#siblings()
*/
private static final class SiblingNodeIterable
implements Iterable<Node>, Iterator<Node> {
private final Node start;
private Node current;
private boolean used;
SiblingNodeIterable(Node start) {
this.start = start;
this.current = start;
this.used = false;
}
@Override
public Iterator<Node> iterator() {
if (!used) {
used = true;
return this;
} else {
// We have already used the current object as an iterator;
// we must create a new SiblingNodeIterable based on this
// iterable's start node.
//
// Since the primary use case for Node.children is in for
// loops, this branch is extremely unlikely.
return (new SiblingNodeIterable(start)).iterator();
}
}
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Node next() {
if (current == null) {
throw new NoSuchElementException();
}
try {
return current;
} finally {
current = current.getNext();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
// ==========================================================================
// Accessors
PropListItem getPropListHeadForTesting() {
return propListHead;
}
public Node getParent() {
return parent;
}
/**
* Gets the ancestor node relative to this.
*
* @param level 0 = this, 1 = the parent, etc.
*/
public Node getAncestor(int level) {
Preconditions.checkArgument(level >= 0);
Node node = this;
while (node != null && level-- > 0) {
node = node.getParent();
}
return node;
}
/**
* Iterates all of the node's ancestors excluding itself.
*/
public AncestorIterable getAncestors() {
return new AncestorIterable(this.getParent());
}
/**
* Iterator to go up the ancestor tree.
*/
public static class AncestorIterable implements Iterable<Node> {
private
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> is not a valid qualified name.
*
* @return a null if this is not a qualified name, or a dot-separated string
* of the name and properties.
*/
public String getQualifiedName() {
if (type == Token.NAME) {
String name = getString();
return name.isEmpty() ? null : name;
} else if (type == Token.GETPROP) {
String left = getFirstChild().getQualifiedName();
if (left == null) {
return null;
}
return left + "." + getLastChild().getString();
} else if (type == Token.THIS) {
return "this";
} else {
return null;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name, such as
* <code>x</code> or <code>a.b.c</code> or <code>this.a</code>.
*/
public boolean isQualifiedName() {
switch (getType()) {
case Token.NAME:
return getString().isEmpty() ? false : true;
case Token.THIS:
return true;
case Token.GETPROP:
return getFirstChild().isQualifiedName();
default:
return false;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name without
* a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code>
* .
*/
public boolean isUnscopedQualifiedName() {
switch (getType()) {
case Token.NAME:
return getString().isEmpty() ? false : true;
case Token.GETPROP:
return getFirstChild().isUnscopedQualifiedName();
default:
return false;
}
}
// ==========================================================================
// Mutators
/**
* Removes this node from its parent. Equivalent to:
* node.getParent().removeChild();
*/
public Node detachFromParent() {
Preconditions.checkState(parent != null);
parent.removeChild(this);
return this;
}
/**
* Removes the first child of Node. Equivalent to:
* node.removeChild(node.getFirstChild());
*
* @return The removed Node.
*/
public Node removeFirstChild() {
Node child = first;
if (child != null)
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> {
removeChild(child);
}
return child;
}
/**
* @return A Node that is the head of the list of children.
*/
public Node removeChildren() {
Node children = first;
for (Node child = first; child != null; child = child.getNext()) {
child.parent = null;
}
first = null;
last = null;
return children;
}
/**
* Removes all children from this node and isolates the children from each
* other.
*/
public void detachChildren() {
for (Node child = first; child != null;) {
Node nextChild = child.getNext();
child.parent = null;
child.next = null;
child = nextChild;
}
first = null;
last = null;
}
public Node removeChildAfter(Node prev) {
Preconditions.checkArgument(prev.parent == this,
"prev is not a child of this node.");
Preconditions.checkArgument(prev.next != null,
"no next sibling.");
Node child = prev.next;
prev.next = child.next;
if (child == last) last = prev;
child.next = null;
child.parent = null;
return child;
}
/**
* @return A detached clone of the Node, specifically excluding its children.
*/
public Node cloneNode() {
Node result;
try {
result = (Node) super.clone();
// PropListItem lists are immutable and can be shared so there is no
// need to clone them here.
result.next = null;
result.first = null;
result.last = null;
result.parent = null;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
/**
* @return A detached clone of the Node and all its children.
*/
public Node cloneTree() {
Node result = cloneNode();
for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) {
Node n2clone = n2.cloneTree();
n2clone.parent = result;
if (result.last != null) {
result.last.next = n2clone;
}
if (result.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>1;
final public static int FLAG_THIS_UNMODIFIED = 2;
final public static int FLAG_ARGUMENTS_UNMODIFIED = 4;
final public static int FLAG_NO_THROWS = 8;
final public static int FLAG_LOCAL_RESULTS = 16;
final public static int SIDE_EFFECTS_FLAGS_MASK = 31;
final public static int SIDE_EFFECTS_ALL = 0;
final public static int NO_SIDE_EFFECTS =
FLAG_GLOBAL_STATE_UNMODIFIED
| FLAG_THIS_UNMODIFIED
| FLAG_ARGUMENTS_UNMODIFIED
| FLAG_NO_THROWS;
/**
* Marks this function or constructor call's side effect flags.
* This property is only meaningful for {@link Token#CALL} and
* {@link Token#NEW} nodes.
*/
public void setSideEffectFlags(int flags) {
Preconditions.checkArgument(
getType() == Token.CALL || getType() == Token.NEW,
"setIsNoSideEffectsCall only supports CALL and NEW nodes, got " +
Token.name(getType()));
putIntProp(SIDE_EFFECT_FLAGS, flags);
}
public void setSideEffectFlags(SideEffectFlags flags) {
setSideEffectFlags(flags.valueOf());
}
/**
* Returns the side effects flags for this node.
*/
public int getSideEffectFlags() {
return getIntProp(SIDE_EFFECT_FLAGS);
}
/**
* A helper class for getting and setting the side-effect flags.
* @author johnlenz@google.com (John Lenz)
*/
public static class SideEffectFlags {
private int value = Node.SIDE_EFFECTS_ALL;
public SideEffectFlags() {
}
public SideEffectFlags(int value) {
this.value = value;
}
public int valueOf() {
return value;
}
/** All side-effect occur and the returned results are non-local. */
public void setAllFlags() {
value = Node.SIDE_EFFECTS_ALL;
}
/** No side-effects occur and the returned results are local. */
public void clearAllFlags() {
value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> false);
}
InstanceObjectType(JSTypeRegistry registry, FunctionType constructor,
boolean isNativeType) {
super(registry, null, null, isNativeType);
Preconditions.checkNotNull(constructor);
this.constructor = constructor;
}
@Override
public String getReferenceName() {
return getConstructor().getReferenceName();
}
@Override
public boolean hasReferenceName() {
return getConstructor().hasReferenceName();
}
@Override
public ObjectType getImplicitPrototype() {
return getConstructor().getPrototype();
}
@Override
public FunctionType getConstructor() {
return constructor;
}
@Override
boolean defineProperty(String name, JSType type, boolean inferred,
Node propertyNode) {
ObjectType proto = getImplicitPrototype();
if (proto != null && proto.hasOwnDeclaredProperty(name)) {
return false;
}
return super.defineProperty(name, type, inferred, propertyNode);
}
@Override
String toStringHelper(boolean forAnnotations) {
if (constructor.hasReferenceName()) {
return constructor.getReferenceName();
} else {
return super.toStringHelper(forAnnotations);
}
}
@Override
boolean isTheObjectType() {
return getConstructor().isNativeObjectType()
&& "Object".equals(getReferenceName());
}
@Override
public boolean isInstanceType() {
return true;
}
@Override
public boolean isArrayType() {
return getConstructor().isNativeObjectType()
&& "Array".equals(getReferenceName());
}
@Override
public boolean isStringObjectType() {
return getConstructor().isNativeObjectType()
&& "String".equals(getReferenceName());
}
@Override
public boolean isBooleanObjectType() {
return getConstructor().isNativeObjectType()
&& "Boolean".equals(getReferenceName());
}
@Override
public boolean isNumberObjectType() {
return getConstructor().isNativeObjectType()
&& "Number".equals(getReferenceName());
}
@Override
public boolean isDateType() {
return getConstructor().isNativeObjectType()
&& "Date".equals(getReferenceName());
}
@Override
public boolean isRegexpType() {
return getConstructor().isNativeObjectType()
&& "RegExp".equals(getReferenceName());
}
@Override
public boolean isNominalType() {
return hasReferenceName
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> public static DiagnosticGroup CHECK_TYPES =
DiagnosticGroups.registerGroup("checkTypes",
TypeValidator.ALL_DIAGNOSTICS,
TypeCheck.ALL_DIAGNOSTICS);
public static DiagnosticGroup CHECK_VARIABLES =
DiagnosticGroups.registerGroup("checkVars",
VarCheck.UNDEFINED_VAR_ERROR,
SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR);
public static DiagnosticGroup CHECK_USELESS_CODE =
DiagnosticGroups.registerGroup("uselessCode",
CheckSideEffects.USELESS_CODE_ERROR,
CheckUnreachableCode.UNREACHABLE_CODE);
public static DiagnosticGroup CONST =
DiagnosticGroups.registerGroup("const",
CheckAccessControls.CONST_PROPERTY_DELETED,
CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE,
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
public static DiagnosticGroup TYPE_INVALIDATION =
DiagnosticGroups.registerGroup("typeInvalidation",
DisambiguateProperties.Warnings.INVALIDATION);
public static DiagnosticGroup DUPLICATE_VARS =
DiagnosticGroups.registerGroup("duplicate",
SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR,
TypeValidator.DUP_VAR_DECLARATION);
public static DiagnosticGroup ES5_STRICT =
DiagnosticGroups.registerGroup("es5Strict",
ControlStructureCheck.USE_OF_WITH,
StrictModeCheck.UNKNOWN_VARIABLE,
StrictModeCheck.EVAL_DECLARATION,
StrictModeCheck.EVAL_ASSIGNMENT,
StrictModeCheck.ARGUMENTS_DECLARATION,
StrictModeCheck.ARGUMENTS_ASSIGNMENT,
StrictModeCheck.DELETE_VARIABLE,
StrictModeCheck.DUPLICATE_OBJECT_KEY);
public static DiagnosticGroup CHECK_PROVIDES =
DiagnosticGroups.registerGroup("checkProvides",
CheckProvides.MISSING_PROVIDE_WARNING);
public static DiagnosticGroup DUPLICATE_MESSAGE =
DiagnosticGroups.registerGroup("duplicateMessage",
JsMessageVisitor.MESSAGE_DUPLICATE_KEY);
/**
* Adds warning levels by name.
*/
void setWarningLevel(CompilerOptions options,
String name, CheckLevel level) {
DiagnosticGroup group = forName(name);
Preconditions.checkNotNull(group, "No warning class for name: %s", name);
options.set
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
* must also look up this map and connect:
* foo() -> bar()
* bar() -> END
*/
private final Multimap<Node, Node> finallyMap = HashMultimap.create();
/**
* Constructor.
*
* @param compiler Compiler instance.
* @param shouldTraverseFunctions Whether functions should be traversed (true
* by default).
* @param edgeAnnotations Whether to allow edge annotations. By default,
* only node annotations are allowed.
*/
ControlFlowAnalysis(AbstractCompiler compiler,
boolean shouldTraverseFunctions, boolean edgeAnnotations) {
this.compiler = compiler;
this.shouldTraverseFunctions = shouldTraverseFunctions;
this.edgeAnnotations = edgeAnnotations;
}
ControlFlowGraph<Node> getCfg() {
return cfg;
}
@Override
public void process(Node externs, Node root) {
this.root = root;
astPositionCounter = 0;
astPosition = Maps.newHashMap();
nodePriorities = Maps.newHashMap();
cfg = new AstControlFlowGraph(computeFallThrough(root), nodePriorities,
edgeAnnotations);
NodeTraversal.traverse(compiler, root, this);
astPosition.put(null, ++astPositionCounter); // the implicit return is last.
// Now, generate the priority of nodes by doing a depth-first
// search on the CFG.
priorityCounter = 0;
DiGraphNode<Node, Branch> entry = cfg.getEntry();
prioritizeFromEntryNode(entry);
if (shouldTraverseFunctions) {
// If we're traversing inner functions, we need to rank the
// priority of them too.
for (DiGraphNode<Node, Branch> candidate : cfg.getDirectedGraphNodes()) {
Node value = candidate.getValue();
if (value != null && value.isFunction()) {
Preconditions.checkState(
!nodePriorities.containsKey(candidate) || candidate == entry);
prioritizeFromEntryNode(candidate);
}
}
}
// At this point, all reachable nodes have been given a priority, but
// unreachable nodes have not been given a priority. Put them last.
// Presumably, it doesn't really matter what priority they get, since
// this shouldn't happen in real code.
for (Di
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> each expression node.
*
* For example: within a Token.SWITCH, the expression in question does not
* change the control flow and need not to be considered.
*/
if (parent != null) {
switch (parent.getType()) {
case Token.FOR:
// Only traverse the body of the for loop.
return n == parent.getLastChild();
// Skip the conditions.
case Token.IF:
case Token.WHILE:
case Token.WITH:
return n != parent.getFirstChild();
case Token.DO:
return n != parent.getFirstChild().getNext();
// Only traverse the body of the cases
case Token.SWITCH:
case Token.CASE:
case Token.CATCH:
case Token.LABEL:
return n != parent.getFirstChild();
case Token.FUNCTION:
return n == parent.getFirstChild().getNext().getNext();
case Token.CONTINUE:
case Token.BREAK:
case Token.EXPR_RESULT:
case Token.VAR:
case Token.RETURN:
case Token.THROW:
return false;
case Token.TRY:
/* Just before we are about to visit the second child of the TRY node,
* we know that we will be visiting either the CATCH or the FINALLY.
* In other words, we know that the post order traversal of the TRY
* block has been finished, no more exceptions can be caught by the
* handler at this TRY block and should be taken out of the stack.
*/
if (n == parent.getFirstChild().getNext()) {
Preconditions.checkState(exceptionHandler.peek() == parent);
exceptionHandler.pop();
}
}
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.IF:
handleIf(n);
return;
case Token.WHILE:
handleWhile(n);
return;
case Token.DO:
handleDo(n);
return;
case Token.FOR:
handleFor(n);
return;
case Token.SWITCH:
handleSwitch(n);
return;
case Token.CASE:
handleCase(n);
return;
case Token.DEFAULT_CASE:
handleDefault(n);
return;
case
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> least one CASE or EMPTY
createEdge(node, Branch.UNCOND, next);
} else { // Has no CASE but possibly a DEFAULT
if (node.getFirstChild().getNext() != null) {
createEdge(node, Branch.UNCOND, node.getFirstChild().getNext());
} else { // No CASE, no DEFAULT
createEdge(node, Branch.UNCOND, computeFollowNode(node, this));
}
}
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
private void handleCase(Node node) {
// Case is a bit tricky....First it goes into the body if condition is true.
createEdge(node, Branch.ON_TRUE,
node.getFirstChild().getNext());
// Look for the next CASE, skipping over DEFAULT.
Node next = getNextSiblingOfType(node.getNext(), Token.CASE);
if (next != null) { // Found a CASE
Preconditions.checkState(next.isCase());
createEdge(node, Branch.ON_FALSE, next);
} else { // No more CASE found, go back and search for a DEFAULT.
Node parent = node.getParent();
Node deflt = getNextSiblingOfType(
parent.getFirstChild().getNext(), Token.DEFAULT_CASE);
if (deflt != null) { // Has a DEFAULT
createEdge(node, Branch.ON_FALSE, deflt);
} else { // No DEFAULT found, go to the follow of the SWITCH.
createEdge(node, Branch.ON_FALSE, computeFollowNode(node, this));
}
}
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
private void handleDefault(Node node) {
// Directly goes to the body. It should not transfer to the next case.
createEdge(node, Branch.UNCOND, node.getFirstChild());
}
private void handleWith(Node node) {
// Directly goes to the body. It should not transfer to the next case.
createEdge(node, Branch.UNCOND, node.getLastChild());
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
private void handleStmtList(Node node) {
Node parent = node.getParent();
// Special case, don't add a block of empty CATCH block to the graph.
if (node.isBlock() &&
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> parent != null &&
parent.isTry() &&
NodeUtil.getCatchBlock(parent) == node &&
!NodeUtil.hasCatchHandler(node)) {
return;
}
// A block transfer control to its first child if it is not empty.
Node child = node.getFirstChild();
// Function declarations are skipped since control doesn't go into that
// function (unless it is called)
while (child != null && child.isFunction()) {
child = child.getNext();
}
if (child != null) {
createEdge(node, Branch.UNCOND, computeFallThrough(child));
} else {
createEdge(node, Branch.UNCOND, computeFollowNode(node, this));
}
// Synthetic blocks
if (parent != null) {
switch (parent.getType()) {
case Token.DEFAULT_CASE:
case Token.CASE:
case Token.TRY:
break;
default:
if (node.isBlock() && node.isSyntheticBlock()) {
createEdge(node, Branch.SYN_BLOCK, computeFollowNode(node, this));
}
break;
}
}
}
private void handleFunction(Node node) {
// A block transfer control to its first child if it is not empty.
Preconditions.checkState(node.getChildCount() >= 3);
createEdge(node, Branch.UNCOND,
computeFallThrough(node.getFirstChild().getNext().getNext()));
Preconditions.checkState(exceptionHandler.peek() == node);
exceptionHandler.pop();
}
private void handleExpr(Node node) {
createEdge(node, Branch.UNCOND, computeFollowNode(node, this));
connectToPossibleExceptionHandler(node, node);
}
private void handleThrow(Node node) {
connectToPossibleExceptionHandler(node, node);
}
private void handleTry(Node node) {
createEdge(node, Branch.UNCOND, node.getFirstChild());
}
private void handleCatch(Node node) {
createEdge(node, Branch.UNCOND, node.getLastChild());
}
private void handleBreak(Node node) {
String label = null;
// See if it is a break with label.
if (node.hasChildren()) {
label = node.getFirstChild().getString();
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> (cur.isTry() && NodeUtil.hasFinally(cur)
&& cur.getLastChild() != previous) {
if (lastJump == node) {
createEdge(lastJump, Branch.UNCOND, cur.getLastChild());
} else {
finallyMap.put(lastJump, computeFallThrough(cur.getLastChild()));
}
lastJump = cur;
}
Preconditions.checkState(parent != null, "Cannot find continue target.");
previous = cur;
}
Node iter = cur;
if (cur.getChildCount() == 4) {
iter = cur.getFirstChild().getNext().getNext();
}
if (lastJump == node) {
createEdge(node, Branch.UNCOND, iter);
} else {
finallyMap.put(lastJump, iter);
}
}
private void handleReturn(Node node) {
Node lastJump = null;
for (Iterator<Node> iter = exceptionHandler.iterator(); iter.hasNext();) {
Node curHandler = iter.next();
if (curHandler.isFunction()) {
break;
}
if (NodeUtil.hasFinally(curHandler)) {
if (lastJump == null) {
createEdge(node, Branch.UNCOND, curHandler.getLastChild());
} else {
finallyMap.put(lastJump,
computeFallThrough(curHandler.getLastChild()));
}
lastJump = curHandler;
}
}
if (node.hasChildren()) {
connectToPossibleExceptionHandler(node, node.getFirstChild());
}
if (lastJump == null) {
createEdge(node, Branch.UNCOND, null);
} else {
finallyMap.put(lastJump, null);
}
}
private void handleStmt(Node node) {
// Simply transfer to the next line.
createEdge(node, Branch.UNCOND, computeFollowNode(node, this));
connectToPossibleExceptionHandler(node, node);
}
static Node computeFollowNode(Node node, ControlFlowAnalysis cfa) {
return computeFollowNode(node, node, cfa);
}
static Node computeFollowNode(Node node) {
return computeFollowNode(node, node, null);
}
/**
* Computes the follow() node of a given node and its parent. There is a
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> side
* effect when calling this function. If this function computed an edge that
* exists a FINALLY, it'll attempt to connect the fromNode to the outer
* FINALLY according to the finallyMap.
*
* @param fromNode The original source node since {@code node} is changed
* during recursion.
* @param node The node that follow() should compute.
*/
private static Node computeFollowNode(
Node fromNode, Node node, ControlFlowAnalysis cfa) {
/*
* This is the case where:
*
* 1. Parent is null implies that we are transferring control to the end of
* the script.
*
* 2. Parent is a function implies that we are transferring control back to
* the caller of the function.
*
* 3. If the node is a return statement, we should also transfer control
* back to the caller of the function.
*
* 4. If the node is root then we have reached the end of what we have been
* asked to traverse.
*
* In all cases we should transfer control to a "symbolic return" node.
* This will make life easier for DFAs.
*/
Node parent = node.getParent();
if (parent == null || parent.isFunction() ||
(cfa != null && node == cfa.root)) {
return null;
}
// If we are just before a IF/WHILE/DO/FOR:
switch (parent.getType()) {
// The follow() of any of the path from IF would be what follows IF.
case Token.IF:
return computeFollowNode(fromNode, parent, cfa);
case Token.CASE:
case Token.DEFAULT_CASE:
// After the body of a CASE, the control goes to the body of the next
// case, without having to go to the case condition.
if (parent.getNext() != null) {
if (parent.getNext().isCase()) {
return parent.getNext().getFirstChild().getNext();
} else if (parent.getNext().isDefaultCase()) {
return parent.getNext().getFirstChild();
} else {
Preconditions.checkState(false, "Not reachable");
}
} else {
return computeFollowNode(fromNode, parent, cfa);
}
break
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.DO:
return computeFallThrough(n.getFirstChild());
case Token.FOR:
if (NodeUtil.isForIn(n)) {
return n.getFirstChild().getNext();
}
return computeFallThrough(n.getFirstChild());
case Token.LABEL:
return computeFallThrough(n.getLastChild());
default:
return n;
}
}
/**
* Connects the two nodes in the control flow graph.
*
* @param fromNode Source.
* @param toNode Destination.
*/
private void createEdge(Node fromNode, ControlFlowGraph.Branch branch,
Node toNode) {
cfg.createNode(fromNode);
cfg.createNode(toNode);
cfg.connectIfNotFound(fromNode, branch, toNode);
}
/**
* Connects cfgNode to the proper CATCH block if target subtree might throw
* an exception. If there are FINALLY blocks reached before a CATCH, it will
* make the corresponding entry in finallyMap.
*/
private void connectToPossibleExceptionHandler(Node cfgNode, Node target) {
if (mayThrowException(target) && !exceptionHandler.isEmpty()) {
Node lastJump = cfgNode;
for (Node handler : exceptionHandler) {
if (handler.isFunction()) {
return;
}
Preconditions.checkState(handler.isTry());
Node catchBlock = NodeUtil.getCatchBlock(handler);
if (!NodeUtil.hasCatchHandler(catchBlock)) { // No catch but a FINALLY.
if (lastJump == cfgNode) {
createEdge(cfgNode, Branch.ON_EX, handler.getLastChild());
} else {
finallyMap.put(lastJump, handler.getLastChild());
}
} else { // Has a catch.
if (lastJump == cfgNode) {
createEdge(cfgNode, Branch.ON_EX, catchBlock);
return;
} else {
finallyMap.put(lastJump, catchBlock);
}
}
lastJump = handler;
}
}
}
/**
* Get the next sibling (including itself) of one of the given types.
*/
private static Node getNextSiblingOfType(Node first, int ... types) {
for (Node c = first; c != null; c = c.getNext()) {
for
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>> priorities,
boolean edgeAnnotations) {
super(entry,
true /* node annotations */, edgeAnnotations);
this.priorities = priorities;
}
@Override
/**
* Returns a node comparator based on the pre-order traversal of the AST.
* @param isForward x 'before' y in the pre-order traversal implies
* x 'less than' y (if true) and x 'greater than' y (if false).
*/
public Comparator<DiGraphNode<Node, Branch>> getOptionalNodeComparator(
boolean isForward) {
if (isForward) {
return new Comparator<DiGraphNode<Node, Branch>>() {
@Override
public int compare(
DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) {
return getPosition(n1) - getPosition(n2);
}
};
} else {
return new Comparator<DiGraphNode<Node, Branch>>() {
@Override
public int compare(
DiGraphNode<Node, Branch> n1, DiGraphNode<Node, Branch> n2) {
return getPosition(n2) - getPosition(n1);
}
};
}
}
/**
* Gets the pre-order traversal position of the given node.
* @return An arbitrary counter used for comparing positions.
*/
private int getPosition(DiGraphNode<Node, Branch> n) {
Integer priority = priorities.get(n);
Preconditions.checkNotNull(priority);
return priority;
}
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> resolvedNamedTypes.values()) {
type.clearResolved();
}
unresolvedNamedTypes.putAll(resolvedNamedTypes);
resolvedNamedTypes.clear();
}
boolean isLastGeneration() {
return lastGeneration;
}
/**
* Sets whether this is the last generation. In the last generation,
* {@link NamedType} warns about unresolved types.
*/
public void setLastGeneration(boolean lastGeneration) {
this.lastGeneration = lastGeneration;
}
/**
* Tells the type system that {@code type} implements interface {@code
* interfaceInstance}.
* {@code inter} must be an ObjectType for the instance of the interface as it
* could be a named type and not yet have the constructor.
*/
void registerTypeImplementingInterface(
FunctionType type, ObjectType interfaceInstance) {
interfaceToImplementors.put(interfaceInstance.getReferenceName(), type);
}
/**
* Returns a collection of types that directly implement {@code
* interfaceInstance}. Subtypes of implementing types are not guaranteed to
* be returned. {@code interfaceInstance} must be an ObjectType for the
* instance of the interface.
*/
public Collection<FunctionType> getDirectImplementors(
ObjectType interfaceInstance) {
return interfaceToImplementors.get(interfaceInstance.getReferenceName());
}
/**
* Records declared global type names. This makes resolution faster
* and more robust in the common case.
*
* @param name The name of the type to be recorded.
* @param t The actual type being associated with the name.
* @return True if this name is not already defined, false otherwise.
*/
public boolean declareType(String name, JSType t) {
if (namesToTypes.containsKey(name)) {
return false;
}
register(t, name);
return true;
}
/**
* Overrides a declared global type name. Throws an exception if this
* type name hasn't been declared yet.
*/
public void overwriteDeclaredType(String name, JSType t) {
Preconditions.checkState(namesToTypes.containsKey(name));
register(t, name);
}
/**
* Records a forward-declared type name. We will not emit errors if this
* type name never resolves to anything.
*/
public void forwardDeclareType(
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>true);
return type;
}
/**
* Creates a constructor function type.
* @param name the function's name or {@code null} to indicate that the
* function is anonymous.
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
* @param parameters the function's parameters or {@code null}
* to indicate that the parameter types are unknown.
* @param returnType the function's return type or {@code null} to indicate
* that the return type is unknown.
*/
public FunctionType createConstructorType(String name, Node source,
Node parameters, JSType returnType) {
return new FunctionType(this, name, source,
createArrowType(parameters, returnType), null,
null, true, false);
}
/**
* Creates an interface function type.
* @param name the function's name
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
*/
public FunctionType createInterfaceType(String name, Node source) {
return FunctionType.forInterface(this, name, source);
}
/**
* Creates a parameterized type.
*/
public ParameterizedType createParameterizedType(
ObjectType objectType, JSType parameterType) {
return new ParameterizedType(this, objectType, parameterType);
}
/**
* Creates a named type.
*/
@VisibleForTesting
public JSType createNamedType(String reference,
String sourceName, int lineno, int charno) {
return new NamedType(this, reference, sourceName, lineno, charno);
}
/**
* Identifies the name of a typedef or enum before we actually declare it.
*/
public void identifyNonNullableName(String name) {
Preconditions.checkNotNull(name);
nonNullableTypeNames.add(name);
}
/**
* Creates a JSType from the nodes representing a type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
public JSType createFromTypeNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Node = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodesInternal(
fieldTypeNode.getLastChild(), sourceName, scope);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) {
// Duplicate field name, warning and skip
reporter.warning(
"Duplicate record field " + fieldName,
sourceName,
n.getLineno(), fieldNameNode.getCharno());
}
}
return builder.build();
}
/**
* Sets the template type name.
*/
public void setTemplateTypeNames(List<String> names) {
Preconditions.checkNotNull(names);
for (String name : names) {
templateTypes.put(name, new TemplateType(this, name));
}
}
/**
* Clears the template type name.
*/
public void clearTemplateTypeNames() {
templateTypes.clear();
}
}
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> fileOverview = null;
String returnDescription = null;
String version = null;
List<String> authors = null;
List<String> sees = null;
}
/**
* A piece of information (found in a marker) which contains a position
* with a string.
*/
public static class StringPosition extends SourcePosition<String> {
}
/**
* A piece of information (found in a marker) which contains a position
* with a string that has no leading or trailing whitespace.
*/
static class TrimmedStringPosition extends StringPosition {
@Override public void setItem(String item) {
Preconditions.checkArgument(
item.charAt(0) != ' ' &&
item.charAt(item.length() - 1) != ' ',
"String has leading or trailing whitespace");
super.setItem(item);
}
}
/**
* A piece of information (found in a marker) which contains a position
* with a name node.
*/
public static class NamePosition extends SourcePosition<Node> {}
/**
* A piece of information (found in a marker) which contains a position
* with a type expression syntax tree.
*/
public static class TypePosition extends SourcePosition<Node> {
private boolean brackets = false;
/** Returns whether the type has curly braces around it. */
public boolean hasBrackets() {
return brackets;
}
void setHasBrackets(boolean newVal) {
brackets = newVal;
}
}
/**
* Defines a class for containing the parsing information
* for this JSDocInfo. For each annotation found in the
* JsDoc, a marker will be created indicating the annotation
* itself, the name of the annotation (if any; for example,
* a @param has a name, but a @return does not), the
* textual description found on that annotation and, if applicable,
* the type declaration. All this information is only collected
* if documentation collection is turned on.
*/
public static final class Marker {
private TrimmedStringPosition annotation = null;
private TrimmedStringPosition name = null;
private SourcePosition<Node> nameNode = null;
private StringPosition description = null;
private TypePosition type = null;
/**
* Gets the position information for the annotation name. (e.g.,
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(
"JSC_ENUM_INITIALIZER_NOT_ENUM",
"enum initializer must be an object literal or an enum");
static final DiagnosticType CTOR_INITIALIZER =
DiagnosticType.warning(
"JSC_CTOR_INITIALIZER_NOT_CTOR",
"Constructor {0} must be initialized at declaration");
static final DiagnosticType IFACE_INITIALIZER =
DiagnosticType.warning(
"JSC_IFACE_INITIALIZER_NOT_IFACE",
"Interface {0} must be initialized at declaration");
static final DiagnosticType CONSTRUCTOR_EXPECTED =
DiagnosticType.warning(
"JSC_REFLECT_CONSTRUCTOR_EXPECTED",
"Constructor expected as first argument");
static final DiagnosticType UNKNOWN_LENDS =
DiagnosticType.warning(
"JSC_UNKNOWN_LENDS",
"Variable {0} not declared before @lends annotation.");
static final DiagnosticType LENDS_ON_NON_OBJECT =
DiagnosticType.warning(
"JSC_LENDS_ON_NON_OBJECT",
"May only lend properties to object types. {0} has type {1}.");
private final AbstractCompiler compiler;
private final ErrorReporter typeParsingErrorReporter;
private final TypeValidator validator;
private final CodingConvention codingConvention;
private final JSTypeRegistry typeRegistry;
private final List<ObjectType> delegateProxyPrototypes = Lists.newArrayList();
private final Map<String, String> delegateCallingConventions =
Maps.newHashMap();
// Simple properties inferred about functions.
private final Map<Node, AstFunctionContents> functionAnalysisResults =
Maps.newHashMap();
/**
* Defer attachment of types to nodes until all type names
* have been resolved. Then, we can resolve the type and attach it.
*/
private class DeferredSetType {
final Node node;
final JSType type;
DeferredSetType(Node node, JSType type) {
Preconditions.checkNotNull(node);
Preconditions.checkNotNull(type);
this.node = node;
this.type = type;
// Other parts of this pass may read off the node.
// (like when we set the LHS of an assign with a typed RHS function.)
node.setJSType(type);
}
void resolve(Scope scope) {
node.setJSType(type
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(fnThisType));
}
}
}
if (parent == null) {
codingConvention.defineDelegateProxyPrototypeProperties(
typeRegistry, newScope, delegateProxyPrototypes,
delegateCallingConventions);
}
return newScope;
}
/**
* Patches a given global scope by removing variables previously declared in
* a script and re-traversing a new version of that script.
*
* @param globalScope The global scope generated by {@code createScope}.
* @param scriptRoot The script that is modified.
*/
void patchGlobalScope(Scope globalScope, Node scriptRoot) {
// Preconditions: This is supposed to be called only on (named) SCRIPT nodes
// and a global typed scope should have been generated already.
Preconditions.checkState(scriptRoot.isScript());
Preconditions.checkNotNull(globalScope);
Preconditions.checkState(globalScope.isGlobal());
String scriptName = NodeUtil.getSourceName(scriptRoot);
Preconditions.checkNotNull(scriptName);
for (Node node : ImmutableList.copyOf(functionAnalysisResults.keySet())) {
if (scriptName.equals(NodeUtil.getSourceName(node))) {
functionAnalysisResults.remove(node);
}
}
(new FirstOrderFunctionAnalyzer(
compiler, functionAnalysisResults)).process(null, scriptRoot);
// TODO(bashir): Variable declaration is not the only side effect of last
// global scope generation but here we only wipe that part off!
// Remove all variables that were previously declared in this scripts.
// First find all vars to remove then remove them because of iterator!
Iterator<Var> varIter = globalScope.getVars();
List<Var> varsToRemove = Lists.newArrayList();
while (varIter.hasNext()) {
Var oldVar = varIter.next();
if (scriptName.equals(oldVar.getInputName())) {
varsToRemove.add(oldVar);
}
}
for (Var var : varsToRemove) {
globalScope.undeclare(var);
globalScope.getTypeOfThis().removeProperty(var.getName());
}
// Now re-traverse the given script.
GlobalScopeBuilder scopeBuilder = new GlobalScopeBuilder(globalScope);
NodeTraversal.traverse(compiler, scriptRoot, scopeBuilder);
}
/**
* Create the outermost
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>lends annotation resolves correctly.
*
* For more information, see
* http://code.google.com/p/closure-compiler/issues/detail?id=314
*/
private List<Node> lentObjectLiterals = null;
/**
* Type-less stubs.
*
* If at the end of traversal, we still don't have types for these
* stubs, then we should declare UNKNOWN types.
*/
private final List<StubDeclaration> stubDeclarations =
Lists.newArrayList();
/**
* The current source file that we're in.
*/
private String sourceName = null;
/**
* The InputId of the current node.
*/
private InputId inputId;
private AbstractScopeBuilder(Scope scope) {
this.scope = scope;
}
void setDeferredType(Node node, JSType type) {
deferredSetTypes.add(new DeferredSetType(node, type));
}
void resolveTypes() {
// Resolve types and attach them to nodes.
for (DeferredSetType deferred : deferredSetTypes) {
deferred.resolve(scope);
}
// Resolve types and attach them to scope slots.
Iterator<Var> vars = scope.getVars();
while (vars.hasNext()) {
vars.next().resolveType(typeParsingErrorReporter);
}
// Tell the type registry that any remaining types
// are unknown.
typeRegistry.resolveTypesInScope(scope);
}
@Override
public final boolean shouldTraverse(NodeTraversal t, Node n,
Node parent) {
inputId = t.getInputId();
if (n.isFunction() ||
n.isScript()) {
Preconditions.checkNotNull(inputId);
sourceName = NodeUtil.getSourceName(n);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
boolean descend = parent == null || !parent.isFunction() ||
n == parent.getFirstChild() || parent == scope.getRootNode();
if (descend) {
// Handle hoisted functions on pre-order traversal, so that they
// get hit before other things in the scope.
if (NodeUtil.isStatementParent(n)) {
for (Node child = n.getFirstChild();
child
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(keyNode, keyType);
}
if (keyType != null && objLitType != null && declareOnOwner) {
// Declare this property on its object literal.
boolean isExtern = keyNode.isFromExterns();
objLitType.defineDeclaredProperty(memberName, keyType, keyNode);
}
}
}
/**
* Returns the type specified in a JSDoc annotation near a GETPROP or NAME.
*
* Extracts type information from either the {@code @type} tag or from
* the {@code @return} and {@code @param} tags.
*/
private JSType getDeclaredTypeInAnnotation(String sourceName,
Node node, JSDocInfo info) {
JSType jsType = null;
Node objNode =
node.isGetProp() ? node.getFirstChild() :
NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() :
null;
if (info != null) {
if (info.hasType()) {
jsType = info.getType().evaluate(scope, typeRegistry);
} else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) {
String fnName = node.getQualifiedName();
jsType = createFunctionTypeFromNodes(
null, fnName, info, node);
}
}
return jsType;
}
/**
* Asserts that it's OK to define this node's name.
* The node should have a source name and be of the specified type.
*/
void assertDefinitionNode(Node n, int type) {
Preconditions.checkState(sourceName != null);
Preconditions.checkState(n.getType() == type);
}
/**
* Defines a catch parameter.
*/
void defineCatch(Node n, Node parent) {
assertDefinitionNode(n, Token.CATCH);
Node catchName = n.getFirstChild();
defineSlot(catchName, n, null);
}
/**
* Defines a VAR initialization.
*/
void defineVar(Node n, Node parent) {
assertDefinitionNode(n, Token.VAR);
JSDocInfo info = n.getJSDocInfo();
if (n.hasMoreThanOneChild()) {
if (info != null) {
// multiple children
compiler.report(JSError.make
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>, but
* are not mutually exclusive:
* - An object literal that needs an enum type attached to it.
* - An assignment expression with an enum tag in the JsDoc.
*
* This function will always create an enum type, so only call it if
* you're sure that's what you want.
*
* @param rValue The node of the enum.
* @param name The enum's name
* @param info The {@link JSDocInfo} attached to the enum definition.
* @param lValueNode The node where this function is being
* assigned.
*/
private EnumType createEnumTypeFromNodes(Node rValue, String name,
JSDocInfo info, Node lValueNode) {
Preconditions.checkNotNull(info);
Preconditions.checkState(info.hasEnumParameterType());
EnumType enumType = null;
if (rValue != null && rValue.isQualifiedName()) {
// Handle an aliased enum.
Var var = scope.getVar(rValue.getQualifiedName());
if (var != null && var.getType() instanceof EnumType) {
enumType = (EnumType) var.getType();
}
}
if (enumType == null) {
JSType elementsType =
info.getEnumParameterType().evaluate(scope, typeRegistry);
enumType = typeRegistry.createEnumType(name, rValue, elementsType);
if (rValue != null && rValue.isObjectLit()) {
// collect enum elements
Node key = rValue.getFirstChild();
while (key != null) {
String keyName = NodeUtil.getStringValue(key);
if (keyName == null) {
// GET and SET don't have a String value;
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else if (!codingConvention.isValidEnumKey(keyName)) {
compiler.report(
JSError.make(sourceName, key, ENUM_NOT_CONSTANT, keyName));
} else {
enumType.defineElement(keyName, key);
}
key = key.getNext();
}
}
}
if (name != null && scope.isGlobal()) {
typeRegistry.declareType(name, enumType.getElementsType());
}
return
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> enumType;
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type or {@code null} if its type is inferred.
* @param name the defining node. It must be a {@link Token#NAME}.
* @param parent the {@code name}'s parent.
* @param type the variable's type. It may be {@code null}, in which case
* the variable's type will be inferred.
*/
private void defineSlot(Node name, Node parent, JSType type) {
defineSlot(name, parent, type, type == null);
}
/**
* Defines a typed variable. The defining node will be annotated with the
* variable's type of {@link JSTypeNative#UNKNOWN_TYPE} if its type is
* inferred.
*
* Slots may be any variable or any qualified name in the global scope.
*
* @param n the defining NAME or GETPROP node.
* @param parent the {@code n}'s parent.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
*/
void defineSlot(Node n, Node parent, JSType type, boolean inferred) {
Preconditions.checkArgument(inferred || type != null);
// Only allow declarations of NAMEs and qualified names.
// Object literal keys will have to compute their names themselves.
if (n.isName()) {
Preconditions.checkArgument(
parent.isFunction() ||
parent.isVar() ||
parent.isParamList() ||
parent.isCatch());
} else {
Preconditions.checkArgument(
n.isGetProp() &&
(parent.isAssign() ||
parent.isExprResult()));
}
defineSlot(n, parent, n.getQualifiedName(), type, inferred);
}
/**
* Defines a symbol in the current scope.
*
* @param n the defining NAME or GETPROP or object literal key node.
* @param parent the {@code n}'s parent.
* @param variableName The name that this should be known by.
* @param type the variable's type. It may be {@code null} if
* {@code inferred} is {@code true}.
* @param inferred Whether the type is inferred or
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> declared.
*/
void defineSlot(Node n, Node parent, String variableName,
JSType type, boolean inferred) {
Preconditions.checkArgument(!variableName.isEmpty());
boolean isGlobalVar = n.isName() && scope.isGlobal();
boolean shouldDeclareOnGlobalThis =
isGlobalVar &&
(parent.isVar() ||
parent.isFunction());
// If n is a property, then we should really declare it in the
// scope where the root object appears. This helps out people
// who declare "global" names in an anonymous namespace.
Scope scopeToDeclareIn = scope;
if (n.isGetProp() && !scope.isGlobal() &&
isQnameRootedInGlobalScope(n)) {
Scope globalScope = scope.getGlobalScope();
// don't try to declare in the global scope if there's
// already a symbol there with this name.
if (!globalScope.isDeclared(variableName, false)) {
scopeToDeclareIn = scope.getGlobalScope();
}
}
// The input may be null if we are working with a AST snippet. So read
// the extern info from the node.
boolean isExtern = n.isFromExterns();
Var newVar = null;
// declared in closest scope?
CompilerInput input = compiler.getInput(inputId);
if (scopeToDeclareIn.isDeclared(variableName, false)) {
Var oldVar = scopeToDeclareIn.getVar(variableName);
newVar = validator.expectUndeclaredVariable(
sourceName, input, n, parent, oldVar, variableName, type);
} else {
if (!inferred) {
setDeferredType(n, type);
}
newVar =
scopeToDeclareIn.declare(variableName, n, type, input, inferred);
if (type instanceof EnumType) {
Node initialValue = newVar.getInitialValue();
boolean isValidValue = initialValue != null &&
(initialValue.isObjectLit() ||
initialValue.isQualifiedName());
if (!isValidValue) {
compiler.report(JSError.make(sourceName, n, ENUM_INITIALIZER));
}
}
}
// We need to do some additional work for constructors and interfaces.
FunctionType fnType = JSType.toMaybeFunctionType(type
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>ClassName));
if (objectType != null) {
FunctionType functionType = objectType.getConstructor();
if (functionType != null) {
FunctionType getterType =
typeRegistry.createFunctionType(objectType);
codingConvention.applySingletonGetter(functionType, getterType,
objectType);
}
}
}
DelegateRelationship delegateRelationship =
codingConvention.getDelegateRelationship(n);
if (delegateRelationship != null) {
applyDelegateRelationship(delegateRelationship);
}
ObjectLiteralCast objectLiteralCast =
codingConvention.getObjectLiteralCast(n);
if (objectLiteralCast != null) {
if (objectLiteralCast.diagnosticType == null) {
ObjectType type = ObjectType.cast(
typeRegistry.getType(objectLiteralCast.typeName));
if (type != null && type.getConstructor() != null) {
setDeferredType(objectLiteralCast.objectNode, type);
} else {
compiler.report(JSError.make(t.getSourceName(), n,
CONSTRUCTOR_EXPECTED));
}
} else {
compiler.report(JSError.make(t.getSourceName(), n,
objectLiteralCast.diagnosticType));
}
}
}
/**
* Apply special properties that only apply to delegates.
*/
private void applyDelegateRelationship(
DelegateRelationship delegateRelationship) {
ObjectType delegatorObject = ObjectType.cast(
typeRegistry.getType(delegateRelationship.delegator));
ObjectType delegateBaseObject = ObjectType.cast(
typeRegistry.getType(delegateRelationship.delegateBase));
ObjectType delegateSuperObject = ObjectType.cast(
typeRegistry.getType(codingConvention.getDelegateSuperclassName()));
if (delegatorObject != null &&
delegateBaseObject != null &&
delegateSuperObject != null) {
FunctionType delegatorCtor = delegatorObject.getConstructor();
FunctionType delegateBaseCtor = delegateBaseObject.getConstructor();
FunctionType delegateSuperCtor = delegateSuperObject.getConstructor();
if (delegatorCtor != null && delegateBaseCtor != null &&
delegateSuperCtor != null) {
FunctionParamBuilder functionParamBuilder =
new FunctionParamBuilder(typeRegistry);
functionParamBuilder.addRequiredParams(
getNativeType(U2U_CONSTRUCTOR_TYPE));
FunctionType findDelegate = typeRegistry.createFunctionType(
typeRegistry.createDefaultObjectUnion
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>(delegateBaseObject),
functionParamBuilder.build());
FunctionType delegateProxy = typeRegistry.createConstructorType(
delegateBaseObject.getReferenceName() + DELEGATE_PROXY_SUFFIX,
null, null, null);
delegateProxy.setPrototypeBasedOn(delegateBaseObject);
codingConvention.applyDelegateRelationship(
delegateSuperObject, delegateBaseObject, delegatorObject,
delegateProxy, findDelegate);
delegateProxyPrototypes.add(delegateProxy.getPrototype());
}
}
}
/**
* Declare the symbol for a qualified name in the global scope.
*
* @param info The doc info for this property.
* @param n A top-level GETPROP node (it should not be contained inside
* another GETPROP).
* @param parent The parent of {@code n}.
* @param rhsValue The node that {@code n} is being initialized to,
* or {@code null} if this is a stub declaration.
*/
void maybeDeclareQualifiedName(NodeTraversal t, JSDocInfo info,
Node n, Node parent, Node rhsValue) {
Node ownerNode = n.getFirstChild();
String ownerName = ownerNode.getQualifiedName();
String qName = n.getQualifiedName();
String propName = n.getLastChild().getString();
Preconditions.checkArgument(qName != null && ownerName != null);
// Precedence of type information on GETPROPs:
// 1) @type annotation / @enum annotation
// 2) ASSIGN to FUNCTION literal
// 3) @param/@return annotation (with no function literal)
// 4) ASSIGN to something marked @const
// 5) ASSIGN to anything else
//
// 1, 3, and 4 are declarations, 5 is inferred, and 2 is a declaration iff
// the function has JsDoc or has not been declared before.
//
// FUNCTION literals are special because TypedScopeCreator is very smart
// about getting as much type information as possible for them.
// Determining type for #1 + #2 + #3 + #4
JSType valueType = getDeclaredType(t.getSourceName(), info, n, rhsValue);
if (valueType == null && rhsValue != null) {
// Determining type for #5
valueType = rhsValue.
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> null || !info.hasTypedefType()) {
return;
}
String typedef = candidate.getQualifiedName();
if (typedef == null) {
return;
}
// TODO(nicksantos|user): This is a terrible, terrible hack
// to bail out on recursive typedefs. We'll eventually need
// to handle these properly.
typeRegistry.declareType(typedef, getNativeType(UNKNOWN_TYPE));
JSType realType = info.getTypedefType().evaluate(scope, typeRegistry);
if (realType == null) {
compiler.report(
JSError.make(
t.getSourceName(), candidate, MALFORMED_TYPEDEF, typedef));
}
typeRegistry.overwriteDeclaredType(typedef, realType);
if (candidate.isGetProp()) {
defineSlot(candidate, candidate.getParent(),
getNativeType(NO_TYPE), false);
}
}
} // end GlobalScopeBuilder
/**
* A shallow traversal of a local scope to find all arguments and
* local variables.
*/
private final class LocalScopeBuilder extends AbstractScopeBuilder {
/**
* @param scope The scope that we're building.
*/
private LocalScopeBuilder(Scope scope) {
super(scope);
}
/**
* Traverse the scope root and build it.
*/
void build() {
NodeTraversal.traverse(compiler, scope.getRootNode(), this);
AstFunctionContents contents =
getFunctionAnalysisResults(scope.getRootNode());
if (contents != null) {
for (String varName : contents.getEscapedVarNames()) {
Var v = scope.getVar(varName);
Preconditions.checkState(v.getScope() == scope);
v.markEscaped();
}
}
}
/**
* Visit a node in a local scope, and add any local variables or catch
* parameters into the local symbol table.
*
* @param t The node traversal.
* @param n The node being visited.
* @param parent The parent of n
*/
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n == scope.getRootNode()) return;
if (n.isParamList() && parent == scope.getRootNode()) {
handleFunctionInputs(parent);
return;
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Creator(compiler);
typedScopeCreator = new MemoizedScopeCreator(internalScopeCreator);
topScope = typedScopeCreator.createScope(root, null);
}
/**
* Regenerates the top scope potentially only for a sub-tree of AST and then
* copies information for the old global scope.
*
* @param compiler The compiler for which the global scope is generated.
* @param scriptRoot The root of the AST used to generate global scope.
*/
void patchGlobalTypedScope(AbstractCompiler compiler, Node scriptRoot) {
Preconditions.checkNotNull(internalScopeCreator);
internalScopeCreator.patchGlobalScope(topScope, scriptRoot);
}
/**
* Gets the scope creator for typed scopes.
*/
MemoizedScopeCreator getTypedScopeCreator() {
return typedScopeCreator;
}
/**
* Gets the global scope, with type information.
*/
Scope getTopScope() {
return topScope;
}
/**
* Gets the checking passes to run.
*
* Checking passes revolve around emitting warnings and errors.
* They also may include pre-processor passes needed to do
* error analysis more effectively.
*
* Clients that only want to analyze code (like IDEs) and not emit
* code will only run checks and not optimizations.
*/
abstract protected List<PassFactory> getChecks();
/**
* Gets the optimization passes to run.
*
* Optimization passes revolve around producing smaller and faster code.
* They should always run after checking passes.
*/
abstract protected List<PassFactory> getOptimizations();
/**
* Gets a graph of the passes run. For debugging.
*/
GraphvizGraph getPassGraph() {
LinkedDirectedGraph<String, String> graph =
LinkedDirectedGraph.createWithoutAnnotations();
Iterable<PassFactory> allPasses =
Iterables.concat(getChecks(), getOptimizations());
String lastPass = null;
String loopStart = null;
for (PassFactory pass : allPasses) {
String passName = pass.getName();
int i = 1;
while (graph.hasNode(passName)) {
passName = pass.getName() + (i++);
}
graph.createNode(passName);
if (loopStart == null && !pass.isOneTime
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>reportCodeChange();
} else if ("setCssNameMapping".equals(methodName)) {
processSetCssNameMapping(t, n, parent);
}
}
}
break;
case Token.ASSIGN:
case Token.NAME:
// If this is an assignment to a provided name, remove the provided
// object.
handleCandidateProvideDefinition(t, n, parent);
break;
case Token.EXPR_RESULT:
handleTypedefDefinition(t, n, parent);
break;
case Token.FUNCTION:
// If this is a declaration of a provided named function, this is an
// error. Hoisted functions will explode if they're provided.
if (t.inGlobalScope() &&
!NodeUtil.isFunctionExpression(n)) {
String name = n.getFirstChild().getString();
ProvidedName pn = providedNames.get(name);
if (pn != null) {
compiler.report(t.makeError(n, FUNCTION_NAMESPACE_ERROR, name));
}
}
break;
case Token.NEW:
trySimplifyNewDate(t, n, parent);
break;
case Token.GETPROP:
if (n.getFirstChild().isName() &&
!parent.isCall() &&
!parent.isAssign() &&
"goog.base".equals(n.getQualifiedName())) {
reportBadBaseClassUse(t, n, "May only be called directly.");
}
break;
}
}
/**
* Handles a goog.require call.
*/
private void processRequireCall(NodeTraversal t, Node n, Node parent) {
Node left = n.getFirstChild();
Node arg = left.getNext();
if (verifyArgument(t, left, arg)) {
String ns = arg.getString();
ProvidedName provided = providedNames.get(ns);
if (provided == null || !provided.isExplicitlyProvided()) {
unrecognizedRequires.add(
new UnrecognizedRequire(n, ns, t.getSourceName()));
} else {
JSModule providedModule = provided.explicitModule;
// This must be non-null, because there was an explicit provide.
Preconditions.checkNotNull(providedModule);
JSModule module = t.getModule();
if (moduleGraph != null &&
module != providedModule &&
!module
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Map(cssRenamingMap);
parent.getParent().removeChild(parent);
compiler.reportCodeChange();
}
}
/**
* Try to simplify "new Date(goog.now())" to "new Date()".
*/
private void trySimplifyNewDate(NodeTraversal t, Node n, Node parent) {
if (!rewriteNewDateGoogNow) {
return;
}
Preconditions.checkArgument(n.isNew());
Node date = n.getFirstChild();
if (!date.isName() || !"Date".equals(date.getString())) {
return;
}
Node callGoogNow = date.getNext();
if (callGoogNow == null || !callGoogNow.isCall() ||
callGoogNow.getNext() != null) {
return;
}
Node googNow = callGoogNow.getFirstChild();
String googNowQName = googNow.getQualifiedName();
if (googNowQName == null || !"goog.now".equals(googNowQName)
|| googNow.getNext() != null) {
return;
}
n.removeChild(callGoogNow);
compiler.reportCodeChange();
}
/**
* Verifies that a provide method call has exactly one argument,
* and that it's a string literal and that the contents of the string are
* valid JS tokens. Reports a compile error if it doesn't.
*
* @return Whether the argument checked out okay
*/
private boolean verifyProvide(NodeTraversal t, Node methodName, Node arg) {
if (!verifyArgument(t, methodName, arg)) {
return false;
}
for (String part : arg.getString().split("\\.")) {
if (!NodeUtil.isValidPropertyName(part)) {
compiler.report(t.makeError(arg, INVALID_PROVIDE_ERROR, part));
return false;
}
}
return true;
}
/**
* Verifies that a method call has exactly one argument, and that it's a
* string literal. Reports a compile error if it doesn't.
*
* @return Whether the argument checked out okay
*/
private boolean verifyArgument(NodeTraversal t, Node methodName, Node arg) {
return verifyArgument(t, methodName, arg, Token.STRING);
}
/**
* Verifies that a method call
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
String prefixNs = ns.substring(0, pos);
pos = ns.indexOf('.', pos + 1);
if (providedNames.containsKey(prefixNs)) {
providedNames.get(prefixNs).addProvide(
node, module, false /* implicit */);
} else {
providedNames.put(
prefixNs,
new ProvidedName(prefixNs, node, module, false /* implicit */));
}
}
}
// -------------------------------------------------------------------------
/**
* Information required to replace a goog.provide call later in the traversal.
*/
private class ProvidedName {
private final String namespace;
// The node and module where the call was explicitly or implicitly
// goog.provided.
private final Node firstNode;
private final JSModule firstModule;
// The node where the call was explicitly goog.provided. May be null
// if the namespace is always provided implicitly.
private Node explicitNode = null;
private JSModule explicitModule = null;
// The candidate definition.
private Node candidateDefinition = null;
// The minimum module where the provide must appear.
private JSModule minimumModule = null;
// The replacement declaration.
private Node replacementNode = null;
ProvidedName(String namespace, Node node, JSModule module,
boolean explicit) {
Preconditions.checkArgument(
node == null /* The base case */ ||
node.isExprResult());
this.namespace = namespace;
this.firstNode = node;
this.firstModule = module;
addProvide(node, module, explicit);
}
/**
* Add an implicit or explicit provide.
*/
void addProvide(Node node, JSModule module, boolean explicit) {
if (explicit) {
Preconditions.checkState(explicitNode == null);
Preconditions.checkArgument(node.isExprResult());
explicitNode = node;
explicitModule = module;
}
updateMinimumModule(module);
}
boolean isExplicitlyProvided() {
return explicitNode != null;
}
/**
* Record function declaration, variable declaration or assignment that
* refers to the same name as the provide statement. Give preference to
* declarations; if no declaration exists, record a reference to an
* assignment so it repurposed later.
*/
void addDefinition(Node node, JSModule module) {
Preconditions.checkArgument(node
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>.isExprResult() || // assign
node.isFunction() ||
node.isVar());
Preconditions.checkArgument(explicitNode != node);
if ((candidateDefinition == null) || !node.isExprResult()) {
candidateDefinition = node;
updateMinimumModule(module);
}
}
private void updateMinimumModule(JSModule newModule) {
if (minimumModule == null) {
minimumModule = newModule;
} else if (moduleGraph != null) {
minimumModule = moduleGraph.getDeepestCommonDependencyInclusive(
minimumModule, newModule);
} else {
// If there is no module graph, then there must be exactly one
// module in the program.
Preconditions.checkState(newModule == minimumModule,
"Missing module graph");
}
}
/**
* Replace the provide statement.
*
* If we're providing a name with no definition, then create one.
* If we're providing a name with a duplicate definition, then make sure
* that definition becomes a declaration.
*/
void replace() {
if (firstNode == null) {
// Don't touch the base case ('goog').
replacementNode = candidateDefinition;
return;
}
// Handle the case where there is a duplicate definition for an explicitly
// provided symbol.
if (candidateDefinition != null && explicitNode != null) {
explicitNode.detachFromParent();
compiler.reportCodeChange();
// Does this need a VAR keyword?
replacementNode = candidateDefinition;
if (candidateDefinition.isExprResult() &&
!candidateDefinition.getFirstChild().isQualifiedName()) {
candidateDefinition.putBooleanProp(Node.IS_NAMESPACE, true);
Node assignNode = candidateDefinition.getFirstChild();
Node nameNode = assignNode.getFirstChild();
if (nameNode.isName()) {
// Need to convert this assign to a var declaration.
Node valueNode = nameNode.getNext();
assignNode.removeChild(nameNode);
assignNode.removeChild(valueNode);
nameNode.addChildToFront(valueNode);
Node varNode = IR.var(nameNode);
varNode.copyInformationFrom(candidateDefinition);
candidateDefinition.getParent().replaceChild(
candidateDefinition, varNode);
nameNode.setJSDocInfo(assignNode.getJSDocInfo());
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>
compiler.reportCodeChange();
replacementNode = varNode;
}
}
} else {
// Handle the case where there's not a duplicate definition.
replacementNode = createDeclarationNode();
if (firstModule == minimumModule) {
firstNode.getParent().addChildBefore(replacementNode, firstNode);
} else {
// In this case, the name was implicitly provided by two independent
// modules. We need to move this code up to a common module.
int indexOfDot = namespace.lastIndexOf('.');
if (indexOfDot == -1) {
// Any old place is fine.
compiler.getNodeForCodeInsertion(minimumModule)
.addChildToBack(replacementNode);
} else {
// Add it after the parent namespace.
ProvidedName parentName =
providedNames.get(namespace.substring(0, indexOfDot));
Preconditions.checkNotNull(parentName);
Preconditions.checkNotNull(parentName.replacementNode);
parentName.replacementNode.getParent().addChildAfter(
replacementNode, parentName.replacementNode);
}
}
if (explicitNode != null) {
explicitNode.detachFromParent();
}
compiler.reportCodeChange();
}
}
/**
* Create the declaration node for this name, without inserting it
* into the AST.
*/
private Node createDeclarationNode() {
if (namespace.indexOf('.') == -1) {
return makeVarDeclNode();
} else {
return makeAssignmentExprNode();
}
}
/**
* Creates a simple namespace variable declaration
* (e.g. <code>var foo = {};</code>).
*/
private Node makeVarDeclNode() {
Node name = IR.name(namespace);
name.addChildToFront(createNamespaceLiteral());
Node decl = IR.var(name);
decl.putBooleanProp(Node.IS_NAMESPACE, true);
// TODO(nicksantos): ew ew ew. Create a mutator package.
if (compiler.getCodingConvention().isConstant(namespace)) {
name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
if (candidateDefinition == null) {
name.setJSDocInfo(createConstantJsDoc());
}
Preconditions.checkState(isNamespacePlaceholder(decl));
setSourceInfo(decl);
return decl
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>;
}
/**
* There are some special cases where clients of the compiler
* do not run TypedScopeCreator after running this pass.
* So always give the namespace literal a type.
*/
private Node createNamespaceLiteral() {
Node objlit = IR.objectlit();
objlit.setJSType(
compiler.getTypeRegistry().createAnonymousObjectType());
return objlit;
}
/**
* Creates a dotted namespace assignment expression
* (e.g. <code>foo.bar = {};</code>).
*/
private Node makeAssignmentExprNode() {
Node decl = IR.exprResult(
IR.assign(
NodeUtil.newQualifiedNameNode(
compiler.getCodingConvention(), namespace,
firstNode /* real source info will be filled in below */,
namespace),
createNamespaceLiteral()));
decl.putBooleanProp(Node.IS_NAMESPACE, true);
if (candidateDefinition == null) {
decl.getFirstChild().setJSDocInfo(createConstantJsDoc());
}
Preconditions.checkState(isNamespacePlaceholder(decl));
setSourceInfo(decl);
return decl;
}
private JSDocInfo createConstantJsDoc() {
JSDocInfoBuilder builder = new JSDocInfoBuilder(false);
builder.recordConstancy();
return builder.build(null);
}
/**
* Copy source info to the new node.
*/
private void setSourceInfo(Node newNode) {
Node provideStringNode = getProvideStringNode();
int offset = getSourceInfoOffset(provideStringNode);
Node sourceInfoNode = provideStringNode == null
? firstNode : provideStringNode;
newNode.copyInformationFromForTree(sourceInfoNode);
if (offset != 0) {
newNode.setSourceEncodedPositionForTree(
sourceInfoNode.getSourcePosition() + offset);
}
}
/**
* Get the offset into the provide node where the symbol appears.
*/
private int getSourceInfoOffset(Node provideStringNode) {
if (provideStringNode == null) {
return 0;
}
int indexOfLastDot = namespace.lastIndexOf('.');
// +1 for the opening quote
// +1 for the dot
// if there's no dot, then the -1 index cancels it out
// so elegant!
return 2
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> this function. It is only relevant for
* constructors and may be {@code null}.
*/
private List<FunctionType> subTypes;
/**
* The template type name. May be {@code null}.
*/
private final ImmutableList<String> templateTypeNames;
/** Creates an instance for a function that might be a constructor. */
FunctionType(JSTypeRegistry registry, String name, Node source,
ArrowType arrowType, ObjectType typeOfThis,
ImmutableList<String> templateTypeNames,
boolean isConstructor, boolean nativeType) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
nativeType);
setPrettyPrint(true);
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkNotNull(arrowType);
this.source = source;
this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY;
if (isConstructor) {
this.typeOfThis = typeOfThis != null ?
typeOfThis : new InstanceObjectType(registry, this, nativeType);
} else {
this.typeOfThis = typeOfThis != null ?
typeOfThis :
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
}
this.call = arrowType;
this.templateTypeNames = templateTypeNames != null
? templateTypeNames : ImmutableList.<String>of();
}
/** Creates an instance for a function that is an interface. */
private FunctionType(JSTypeRegistry registry, String name, Node source) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE));
setPrettyPrint(true);
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkArgument(name != null);
this.source = source;
this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null);
this.kind = Kind.INTERFACE;
this.typeOfThis = new InstanceObjectType(registry, this);
this.templateTypeNames = ImmutableList.of();
}
/** Creates an instance for a function that is an interface. */
static FunctionType forInterface(
JSTypeRegistry registry, String name, Node source) {
return new FunctionType(registry, name
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS> }
return builder.build();
}
@Override
boolean defineProperty(String name, JSType type,
boolean inferred, Node propertyNode) {
if ("prototype".equals(name)) {
ObjectType objType = type.toObjectType();
if (objType != null) {
if (prototypeSlot != null &&
objType.isEquivalentTo(prototypeSlot.getType())) {
return true;
}
this.setPrototypeBasedOn(objType, propertyNode);
return true;
} else {
return false;
}
}
return super.defineProperty(name, type, inferred, propertyNode);
}
/**
* Computes the supremum or infimum of two functions.
* Because sup() and inf() share a lot of logic for functions, we use
* a single helper.
* @param leastSuper If true, compute the supremum of {@code this} with
* {@code that}. Otherwise, compute the infimum.
* @return The least supertype or greatest subtype.
*/
FunctionType supAndInfHelper(FunctionType that, boolean leastSuper) {
// NOTE(nicksantos): When we remove the unknown type, the function types
// form a lattice with the universal constructor at the top of the lattice,
// and the LEAST_FUNCTION_TYPE type at the bottom of the lattice.
//
// When we introduce the unknown type, it's much more difficult to make
// heads or tails of the partial ordering of types, because there's no
// clear hierarchy between the different components (parameter types and
// return types) in the ArrowType.
//
// Rather than make the situation more complicated by introducing new
// types (like unions of functions), we just fallback on the simpler
// approach of getting things right at the top and the bottom of the
// lattice.
//
// If there are unknown parameters or return types making things
// ambiguous, then sup(A, B) is always the top function type, and
// inf(A, B) is always the bottom function type.
Preconditions.checkNotNull(that);
if (isEquivalentTo(that)) {
return this;
}
// If these are ordinary functions, then merge them.
// Don't do this if any of the params/return
// values are unknown, because then there will be
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>ParamsNode = call.parameters;
} else {
// If the parameters are not equal, don't try to merge them.
// Someday, we should try to merge the individual params.
return null;
}
JSType newReturnType = leastSuper ?
call.returnType.getLeastSupertype(other.call.returnType) :
call.returnType.getGreatestSubtype(other.call.returnType);
ObjectType newTypeOfThis = null;
if (isEquivalent(typeOfThis, other.typeOfThis)) {
newTypeOfThis = typeOfThis;
} else {
JSType maybeNewTypeOfThis = leastSuper ?
typeOfThis.getLeastSupertype(other.typeOfThis) :
typeOfThis.getGreatestSubtype(other.typeOfThis);
if (maybeNewTypeOfThis instanceof ObjectType) {
newTypeOfThis = (ObjectType) maybeNewTypeOfThis;
} else {
newTypeOfThis = leastSuper ?
registry.getNativeObjectType(JSTypeNative.OBJECT_TYPE) :
registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE);
}
}
boolean newReturnTypeInferred =
call.returnTypeInferred || other.call.returnTypeInferred;
return new FunctionType(
registry, null, null,
new ArrowType(
registry, newParamsNode, newReturnType, newReturnTypeInferred),
newTypeOfThis, null, false, false);
}
/**
* Given a constructor or an interface type, get its superclass constructor
* or {@code null} if none exists.
*/
public FunctionType getSuperClassConstructor() {
Preconditions.checkArgument(isConstructor() || isInterface());
ObjectType maybeSuperInstanceType = getPrototype().getImplicitPrototype();
if (maybeSuperInstanceType == null) {
return null;
}
return maybeSuperInstanceType.getConstructor();
}
/**
* Given an interface and a property, finds the top-most super interface
* that has the property defined (including this interface).
*/
public static ObjectType getTopDefiningInterface(ObjectType type,
String propertyName) {
ObjectType foundType = null;
if (type.hasProperty(propertyName)) {
foundType = type;
}
for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) {
if (interfaceType.hasProperty(propertyName)) {
found
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>Type = getTopDefiningInterface(interfaceType, propertyName);
}
}
return foundType;
}
/**
* Given a constructor or an interface type and a property, finds the
* top-most superclass that has the property defined (including this
* constructor).
*/
public ObjectType getTopMostDefiningType(String propertyName) {
Preconditions.checkState(isConstructor() || isInterface());
Preconditions.checkArgument(getInstanceType().hasProperty(propertyName));
FunctionType ctor = this;
if (isInterface()) {
return getTopDefiningInterface(this.getInstanceType(), propertyName);
}
ObjectType topInstanceType = null;
do {
topInstanceType = ctor.getInstanceType();
ctor = ctor.getSuperClassConstructor();
} while (ctor != null
&& ctor.getPrototype().hasProperty(propertyName));
return topInstanceType;
}
/**
* Two function types are equal if their signatures match. Since they don't
* have signatures, two interfaces are equal if their names match.
*/
@Override
public boolean isEquivalentTo(JSType otherType) {
FunctionType that =
JSType.toMaybeFunctionType(otherType);
if (that == null) {
return false;
}
if (this.isConstructor()) {
if (that.isConstructor()) {
return this == that;
}
return false;
}
if (this.isInterface()) {
if (that.isInterface()) {
return this.getReferenceName().equals(that.getReferenceName());
}
return false;
}
if (that.isInterface()) {
return false;
}
return this.typeOfThis.isEquivalentTo(that.typeOfThis) &&
this.call.isEquivalentTo(that.call);
}
@Override
public int hashCode() {
return isInterface() ? getReferenceName().hashCode() : call.hashCode();
}
public boolean hasEqualCallType(FunctionType otherType) {
return this.call.isEquivalentTo(otherType.call);
}
/**
* Informally, a function is represented by
* {@code function (params): returnType} where the {@code params} is a comma
* separated list of types, the first one being a special
* {@code this:T} if the function expects a known type
Closure, 16
<FILEB>
<CHANGES>
private final Node aliasDefinition;
<CHANGEE>
<CHANGES>
AliasedTypeNode(Node typeReference, Node aliasDefinition,
<CHANGEE>
<CHANGES>
this.aliasDefinition = aliasDefinition;
<CHANGEE>
<CHANGES>
String typeName = typeReference.getString();
String aliasExpanded =
Preconditions.checkNotNull(aliasDefinition.getQualifiedName());
Preconditions.checkState(typeName.startsWith(aliasName));
typeReference.setString(typeName.replaceFirst(aliasName, aliasExpanded));
<CHANGEE>
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode, baseName));
<CHANGEE>
<FILEE>
<FILEB>
private interface AliasUsage {
public void applyAlias();
}
private class AliasedNode implements AliasUsage {
private final Node aliasReference;
private final Node aliasDefinition;
AliasedNode(Node aliasReference, Node aliasDefinition) {
this.aliasReference = aliasReference;
this.aliasDefinition = aliasDefinition;
}
@Override
public void applyAlias() {
aliasReference.getParent().replaceChild(
aliasReference, aliasDefinition.cloneTree());
}
}
private class AliasedTypeNode implements AliasUsage {
private final Node typeReference;
<CHANGES>
<CHANGEE>
private final String aliasName;
<CHANGES>
AliasedTypeNode(Node typeReference,
<CHANGEE>
String aliasName) {
this.typeReference = typeReference;
<CHANGES>
<CHANGEE>
this.aliasName = aliasName;
}
@Override
public void applyAlias() {
<CHANGES>
typeReference.setString(aliasName);
<CHANGEE>
}
}
private class Traversal implements NodeTraversal.ScopedCallback {
// The job of this class is to collect these three data sets.
// The order of this list determines the order that aliases are applied.
private final List<Node> aliasDefinitionsInOrder = Lists.newArrayList();
private final List<Node> scopeCalls = Lists.newArrayList();
private final List<AliasUsage> aliasUsages = Lists.newArrayList();
// This map is temporary and cleared for each scope.
private final Map<String, Var> aliases = Maps.newHashMap();
// Suppose you create an alias.
// var x = goog.x;
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex == -1) {
endIndex = name.length();
}
String baseName = name.substring(0, endIndex);
Var aliasVar = aliases.get(baseName);
if (aliasVar != null) {
Node aliasedNode = aliasVar.getInitialValue();
<CHANGES>
aliasUsages.add(new AliasedTypeNode(typeNode, aliasedNode<SCANS>variant of the other,
// then we'll treat them as covariant (see comment above).
other.typeOfThis.isSubtype(this.typeOfThis) ||
this.typeOfThis.isSubtype(other.typeOfThis);
return treatThisTypesAsCovariant && this.call.isSubtype(other.call);
}
return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that);
}
@Override
public <T> T visit(Visitor<T> visitor) {
return visitor.caseFunctionType(this);
}
/**
* Gets the type of instance of this function.
* @throws IllegalStateException if this function is not a constructor
* (see {@link #isConstructor()}).
*/
public ObjectType getInstanceType() {
Preconditions.checkState(hasInstanceType());
return typeOfThis;
}
/**
* Sets the instance type. This should only be used for special
* native types.
*/
void setInstanceType(ObjectType instanceType) {
typeOfThis = instanceType;
}
/**
* Returns whether this function type has an instance type.
*/
public boolean hasInstanceType() {
return isConstructor() || isInterface();
}
/**
* Gets the type of {@code this} in this function.
*/
@Override
public ObjectType getTypeOfThis() {
return typeOfThis.isNoObjectType() ?
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE) : typeOfThis;
}
/**
* Gets the source node or null if this is an unknown function.
*/
public Node getSource() {
return source;
}
/**
* Sets the source node.
*/
public void setSource(Node source) {
if (prototypeSlot != null) {
// NOTE(bashir): On one hand when source is null we want to drop any
// references to old nodes retained in prototypeSlot. On the other hand
// we cannot simply drop prototypeSlot, so we retain all information
// except the propertyNode for which we use an approximation! These
// details mostly matter in hot-swap passes.
if (source == null || prototypeSlot.getNode() == null) {
prototypeSlot = new Property(prototypeSlot.getName(),
prototypeSlot.getType(), prototypeSlot.isTypeInferred(), source);
}